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)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
40 // Ensure that argument checks on methods don't overflow
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
60 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
62 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
65 [NonSerialized] private int length;
66 [NonSerialized] private char start_char;
68 public static readonly String Empty = "";
70 public static unsafe bool Equals (string a, string b)
72 if ((a as object) == (b as object))
75 if (a == null || b == null)
83 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
88 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
89 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
90 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
91 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
100 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
101 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
110 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
118 return len == 0 || *s1_ptr == *s2_ptr;
122 public static bool operator == (String a, String b)
124 return Equals (a, b);
127 public static bool operator != (String a, String b)
129 return !Equals (a, b);
133 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
135 public override bool Equals (Object obj)
137 return Equals (this, obj as String);
141 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
143 public bool Equals (String value)
145 return Equals (this, value);
148 [IndexerName ("Chars")]
149 public unsafe char this [int index] {
151 if (index < 0 || index >= length)
152 throw new IndexOutOfRangeException ();
153 fixed (char* c = &start_char)
158 public Object Clone ()
163 public TypeCode GetTypeCode ()
165 return TypeCode.String;
168 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
170 if (destination == null)
171 throw new ArgumentNullException ("destination");
173 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
174 if (destinationIndex < 0)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
177 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
178 if (sourceIndex > Length - count)
179 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
180 if (destinationIndex > destination.Length - count)
181 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
183 fixed (char* dest = destination, src = this)
184 CharCopy (dest + destinationIndex, src + sourceIndex, count);
187 public unsafe char[] ToCharArray ()
189 char[] tmp = new char [length];
190 fixed (char* dest = tmp, src = this)
191 CharCopy (dest, src, length);
195 public unsafe char[] ToCharArray (int startIndex, int length)
198 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
200 throw new ArgumentOutOfRangeException ("length", "< 0");
201 if (startIndex > this.length - length)
202 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
204 char[] tmp = new char [length];
205 fixed (char* dest = tmp, src = this)
206 CharCopy (dest, src + startIndex, length);
210 public String [] Split (params char [] separator)
212 return Split (separator, Int32.MaxValue);
215 public String[] Split (char[] separator, int count)
217 if (separator == null || separator.Length == 0)
218 separator = WhiteChars;
221 throw new ArgumentOutOfRangeException ("count");
224 return new String[0];
227 return new String[1] { this };
229 return InternalSplit (separator, count, 0);
234 [MonoDocumentationNote ("code should be moved to managed")]
235 public String[] Split (char[] separator, int count, StringSplitOptions options)
237 if (separator == null || separator.Length == 0)
238 return Split (WhiteChars, count, options);
241 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
242 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
243 throw new ArgumentException ("Illegal enum value: " + options + ".");
246 return new string [0];
248 return InternalSplit (separator, count, (int)options);
252 public String[] Split (string[] separator, int count, StringSplitOptions options)
254 if (separator == null || separator.Length == 0)
255 return Split (WhiteChars, count, options);
258 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
259 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
260 throw new ArgumentException ("Illegal enum value: " + options + ".");
262 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
264 if (count == 0 || (this == String.Empty && removeEmpty))
265 return new String [0];
267 ArrayList arr = new ArrayList ();
271 while (pos < this.Length) {
273 int matchPos = Int32.MaxValue;
275 // Find the first position where any of the separators matches
276 for (int i = 0; i < separator.Length; ++i) {
277 string sep = separator [i];
278 if (sep == null || sep == String.Empty)
281 int match = IndexOf (sep, pos);
282 if (match > -1 && match < matchPos) {
288 if (matchIndex == -1)
291 if (!(matchPos == pos && removeEmpty))
292 arr.Add (this.Substring (pos, matchPos - pos));
294 pos = matchPos + separator [matchIndex].Length;
298 if (matchCount == count - 1)
303 return new String [] { this };
305 if (removeEmpty && pos == this.Length) {
306 String[] res = new String [arr.Count];
307 arr.CopyTo (0, res, 0, arr.Count);
312 String[] res = new String [arr.Count + 1];
313 arr.CopyTo (0, res, 0, arr.Count);
314 res [arr.Count] = this.Substring (pos);
322 public String[] Split (char[] separator, StringSplitOptions options)
324 return Split (separator, Int32.MaxValue, options);
328 public String[] Split (String[] separator, StringSplitOptions options)
330 return Split (separator, Int32.MaxValue, options);
334 public String Substring (int startIndex)
339 if (startIndex < 0 || startIndex > this.length)
340 throw new ArgumentOutOfRangeException ("startIndex");
343 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
345 if (startIndex > this.length)
346 throw new ArgumentOutOfRangeException ("length", "Cannot exceed length of string.");
349 return SubstringUnchecked (startIndex, this.length - startIndex);
352 public String Substring (int startIndex, int length)
355 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
357 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
359 if (startIndex > this.length)
360 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
362 if (startIndex > this.length - length)
363 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
365 if (startIndex == 0 && length == this.length)
369 return SubstringUnchecked (startIndex, length);
372 // This method is used by StringBuilder.ToString() and is expected to
373 // always create a new string object (or return String.Empty).
374 internal unsafe String SubstringUnchecked (int startIndex, int length)
379 string tmp = InternalAllocateStr (length);
380 fixed (char* dest = tmp, src = this) {
381 CharCopy (dest, src + startIndex, length);
386 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
388 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
390 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
391 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
392 (char) 0x3000, (char) 0xFEFF };
394 public String Trim ()
398 int start = FindNotWhiteSpace (0, length, 1);
403 int end = FindNotWhiteSpace (length - 1, start, -1);
405 int newLength = end - start + 1;
406 if (newLength == length)
409 return SubstringUnchecked (start, newLength);
412 public String Trim (params char[] trimChars)
414 if (trimChars == null || trimChars.Length == 0)
419 int start = FindNotInTable (0, length, 1, trimChars);
424 int end = FindNotInTable (length - 1, start, -1, trimChars);
426 int newLength = end - start + 1;
427 if (newLength == length)
430 return SubstringUnchecked (start, newLength);
433 public String TrimStart (params char[] trimChars)
438 if (trimChars == null || trimChars.Length == 0)
439 start = FindNotWhiteSpace (0, length, 1);
441 start = FindNotInTable (0, length, 1, trimChars);
446 return SubstringUnchecked (start, length - start);
449 public String TrimEnd (params char[] trimChars)
454 if (trimChars == null || trimChars.Length == 0)
455 end = FindNotWhiteSpace (length - 1, -1, -1);
457 end = FindNotInTable (length - 1, -1, -1, trimChars);
463 return SubstringUnchecked (0, end);
466 private int FindNotWhiteSpace (int pos, int target, int change)
468 while (pos != target) {
472 if (c < 0x9 || c > 0xD)
477 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
479 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
481 if (c < 0x2000 || c > 0x200B)
490 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
492 fixed (char* tablePtr = table, thisPtr = this) {
493 while (pos != target) {
494 char c = thisPtr[pos];
496 while (x < table.Length) {
497 if (c == tablePtr[x])
501 if (x == table.Length)
509 public static int Compare (String strA, String strB)
511 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
514 public static int Compare (String strA, String strB, bool ignoreCase)
516 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
519 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
522 throw new ArgumentNullException ("culture");
524 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
527 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
529 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
532 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
534 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
537 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
540 throw new ArgumentNullException ("culture");
542 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
543 throw new ArgumentOutOfRangeException ();
555 else if (strB == null) {
559 CompareOptions compopts;
562 compopts = CompareOptions.IgnoreCase;
564 compopts = CompareOptions.None;
566 // Need to cap the requested length to the
567 // length of the string, because
568 // CompareInfo.Compare will insist that length
569 // <= (string.Length - offset)
574 if (length > (strA.Length - indexA)) {
575 len1 = strA.Length - indexA;
578 if (length > (strB.Length - indexB)) {
579 len2 = strB.Length - indexB;
582 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
583 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
586 public static int Compare (string strA, string strB, StringComparison comparisonType)
588 switch (comparisonType) {
589 case StringComparison.CurrentCulture:
590 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
591 case StringComparison.CurrentCultureIgnoreCase:
592 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
593 case StringComparison.InvariantCulture:
594 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
595 case StringComparison.InvariantCultureIgnoreCase:
596 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
597 case StringComparison.Ordinal:
598 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
599 case StringComparison.OrdinalIgnoreCase:
600 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
602 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
603 throw new ArgumentException (msg, "comparisonType");
607 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
609 switch (comparisonType) {
610 case StringComparison.CurrentCulture:
611 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
612 case StringComparison.CurrentCultureIgnoreCase:
613 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
614 case StringComparison.InvariantCulture:
615 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
616 case StringComparison.InvariantCultureIgnoreCase:
617 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
618 case StringComparison.Ordinal:
619 return CompareOrdinal (strA, indexA, strB, indexB, length);
620 case StringComparison.OrdinalIgnoreCase:
621 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
623 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
624 throw new ArgumentException (msg, "comparisonType");
628 public static bool Equals (string a, string b, StringComparison comparisonType)
630 return String.Compare (a, b, comparisonType) == 0;
633 public bool Equals (string value, StringComparison comparisonType)
635 return String.Compare (value, this, comparisonType) == 0;
638 public int CompareTo (Object value)
643 if (!(value is String))
644 throw new ArgumentException ();
646 return String.Compare (this, (String) value);
649 public int CompareTo (String strB)
654 return Compare (this, strB);
657 public static int CompareOrdinal (String strA, String strB)
659 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
662 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
664 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
665 throw new ArgumentOutOfRangeException ();
667 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
670 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
672 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
673 throw new ArgumentOutOfRangeException ();
675 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
678 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
685 } else if (strB == null) {
688 int lengthA = Math.Min (lenA, strA.Length - indexA);
689 int lengthB = Math.Min (lenB, strB.Length - indexB);
691 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
694 fixed (char* aptr = strA, bptr = strB) {
695 char* ap = aptr + indexA;
696 char* end = ap + Math.Min (lengthA, lengthB);
697 char* bp = bptr + indexB;
704 return lengthA - lengthB;
708 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
710 // Same as above, but checks versus uppercase characters
716 } else if (strB == null) {
719 int lengthA = Math.Min (lenA, strA.Length - indexA);
720 int lengthB = Math.Min (lenB, strB.Length - indexB);
722 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
725 fixed (char* aptr = strA, bptr = strB) {
726 char* ap = aptr + indexA;
727 char* end = ap + Math.Min (lengthA, lengthB);
728 char* bp = bptr + indexB;
731 char c1 = Char.ToUpperInvariant (*ap);
732 char c2 = Char.ToUpperInvariant (*bp);
739 return lengthA - lengthB;
743 public bool EndsWith (String value)
746 throw new ArgumentNullException ("value");
748 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
756 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
759 throw new ArgumentNullException ("value");
761 culture = CultureInfo.CurrentCulture;
763 return culture.CompareInfo.IsSuffix (this, value,
764 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
767 // Following methods are culture-insensitive
768 public int IndexOfAny (char [] anyOf)
771 throw new ArgumentNullException ();
772 if (this.length == 0)
775 return IndexOfAnyUnchecked (anyOf, 0, this.length);
778 public int IndexOfAny (char [] anyOf, int startIndex)
781 throw new ArgumentNullException ();
782 if (startIndex < 0 || startIndex > this.length)
783 throw new ArgumentOutOfRangeException ();
785 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
788 public int IndexOfAny (char [] anyOf, int startIndex, int count)
791 throw new ArgumentNullException ();
792 if (startIndex < 0 || startIndex > this.length)
793 throw new ArgumentOutOfRangeException ();
794 if (count < 0 || startIndex > this.length - count)
795 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
797 return IndexOfAnyUnchecked (anyOf, startIndex, count);
800 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
802 if (anyOf.Length == 0)
805 if (anyOf.Length == 1)
806 return IndexOfUnchecked (anyOf[0], startIndex, count);
808 fixed (char* any = anyOf) {
812 char* end_any_ptr = any + anyOf.Length;
814 while (++any_ptr != end_any_ptr) {
815 if (*any_ptr > highest) {
820 if (*any_ptr < lowest)
824 fixed (char* start = &start_char) {
825 char* ptr = start + startIndex;
826 char* end_ptr = ptr + count;
828 while (ptr != end_ptr) {
829 if (*ptr > highest || *ptr < lowest) {
835 return (int)(ptr - start);
838 while (++any_ptr != end_any_ptr) {
839 if (*ptr == *any_ptr)
840 return (int)(ptr - start);
852 public int IndexOf (string value, StringComparison comparisonType)
854 return IndexOf (value, 0, this.Length, comparisonType);
857 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
859 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
862 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
864 switch (comparisonType) {
865 case StringComparison.CurrentCulture:
866 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
867 case StringComparison.CurrentCultureIgnoreCase:
868 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
869 case StringComparison.InvariantCulture:
870 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
871 case StringComparison.InvariantCultureIgnoreCase:
872 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
873 case StringComparison.Ordinal:
874 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
875 case StringComparison.OrdinalIgnoreCase:
876 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
878 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
879 throw new ArgumentException (msg, "comparisonType");
884 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
887 throw new ArgumentNullException ("value");
889 throw new ArgumentOutOfRangeException ("startIndex");
890 if (count < 0 || (this.length - startIndex) < count)
891 throw new ArgumentOutOfRangeException ("count");
893 if (options == CompareOptions.Ordinal)
894 return IndexOfOrdinalUnchecked (value, startIndex, count);
895 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
898 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
900 int valueLen = value.Length;
901 if (count < valueLen)
906 return IndexOfUnchecked (value[0], startIndex, count);
910 fixed (char* thisptr = this, valueptr = value) {
911 char* ap = thisptr + startIndex;
912 char* thisEnd = ap + count - valueLen + 1;
913 while (ap != thisEnd) {
914 if (*ap == *valueptr) {
915 for (int i = 1; i < valueLen; i++) {
916 if (ap[i] != valueptr[i])
919 return (int)(ap - thisptr);
928 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
930 int valueLen = value.Length;
931 if (count < valueLen)
937 fixed (char* thisptr = this, valueptr = value) {
938 char* ap = thisptr + startIndex;
939 char* thisEnd = ap + count - valueLen + 1;
940 while (ap != thisEnd) {
941 for (int i = 0; i < valueLen; i++) {
942 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
945 return (int)(ap - thisptr);
955 public int LastIndexOf (string value, StringComparison comparisonType)
957 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
960 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
962 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
965 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
967 switch (comparisonType) {
968 case StringComparison.CurrentCulture:
969 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
970 case StringComparison.CurrentCultureIgnoreCase:
971 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
972 case StringComparison.InvariantCulture:
973 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
974 case StringComparison.InvariantCultureIgnoreCase:
975 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
976 case StringComparison.Ordinal:
977 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
978 case StringComparison.OrdinalIgnoreCase:
979 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
981 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
982 throw new ArgumentException (msg, "comparisonType");
987 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
990 throw new ArgumentNullException ("value");
991 if (startIndex < 0 || startIndex > length)
992 throw new ArgumentOutOfRangeException ("startIndex");
993 if (count < 0 || (startIndex < count - 1))
994 throw new ArgumentOutOfRangeException ("count");
996 if (options == CompareOptions.Ordinal)
997 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
998 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1001 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1003 int valueLen = value.Length;
1004 if (count < valueLen)
1007 if (valueLen <= 1) {
1009 return LastIndexOfUnchecked (value[0], startIndex, count);
1013 fixed (char* thisptr = this, valueptr = value) {
1014 char* ap = thisptr + startIndex - valueLen + 1;
1015 char* thisEnd = ap - count + valueLen - 1;
1016 while (ap != thisEnd) {
1017 if (*ap == *valueptr) {
1018 for (int i = 1; i < valueLen; i++) {
1019 if (ap[i] != valueptr[i])
1022 return (int)(ap - thisptr);
1031 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1033 int valueLen = value.Length;
1034 if (count < valueLen)
1040 fixed (char* thisptr = this, valueptr = value) {
1041 char* ap = thisptr + startIndex - valueLen + 1;
1042 char* thisEnd = ap - count + valueLen - 1;
1043 while (ap != thisEnd) {
1044 for (int i = 0; i < valueLen; i++) {
1045 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1048 return (int)(ap - thisptr);
1056 // Following methods are culture-insensitive
1057 public int IndexOf (char value)
1059 if (this.length == 0)
1062 return IndexOfUnchecked (value, 0, this.length);
1065 public int IndexOf (char value, int startIndex)
1068 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1069 if (startIndex > this.length)
1070 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1072 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1075 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1078 public int IndexOf (char value, int startIndex, int count)
1080 if (startIndex < 0 || startIndex > this.length)
1081 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1083 throw new ArgumentOutOfRangeException ("count", "< 0");
1084 if (startIndex > this.length - count)
1085 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1087 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1090 return IndexOfUnchecked (value, startIndex, count);
1093 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1095 // It helps JIT compiler to optimize comparison
1096 int value_32 = (int)value;
1098 fixed (char* start = &start_char) {
1099 char* ptr = start + startIndex;
1100 char* end_ptr = ptr + (count >> 3 << 3);
1102 while (ptr != end_ptr) {
1103 if (*ptr == value_32)
1104 return (int)(ptr - start);
1105 if (ptr[1] == value_32)
1106 return (int)(ptr - start + 1);
1107 if (ptr[2] == value_32)
1108 return (int)(ptr - start + 2);
1109 if (ptr[3] == value_32)
1110 return (int)(ptr - start + 3);
1111 if (ptr[4] == value_32)
1112 return (int)(ptr - start + 4);
1113 if (ptr[5] == value_32)
1114 return (int)(ptr - start + 5);
1115 if (ptr[6] == value_32)
1116 return (int)(ptr - start + 6);
1117 if (ptr[7] == value_32)
1118 return (int)(ptr - start + 7);
1123 end_ptr += count & 0x07;
1124 while (ptr != end_ptr) {
1125 if (*ptr == value_32)
1126 return (int)(ptr - start);
1134 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1138 int end = startIndex + count;
1139 char c = Char.ToUpperInvariant (value);
1140 fixed (char* s = &start_char) {
1141 for (int i = startIndex; i < end; i++)
1142 if (Char.ToUpperInvariant (s [i]) == c)
1148 // Following methods are culture-sensitive
1149 public int IndexOf (String value)
1152 throw new ArgumentNullException ("value");
1153 if (value.length == 0)
1155 if (this.length == 0)
1157 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1160 public int IndexOf (String value, int startIndex)
1162 return IndexOf (value, startIndex, this.length - startIndex);
1165 public int IndexOf (String value, int startIndex, int count)
1169 throw new ArgumentNullException ("value");
1171 throw new ArgumentNullException ("string2");
1173 if (startIndex < 0 || startIndex > this.length)
1174 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1175 if (count < 0 || startIndex > this.length - count)
1176 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1178 if (value.length == 0)
1181 if (startIndex == 0 && this.length == 0)
1187 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1190 // Following methods are culture-insensitive
1191 public int LastIndexOfAny (char [] anyOf)
1194 throw new ArgumentNullException ();
1196 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1199 public int LastIndexOfAny (char [] anyOf, int startIndex)
1202 throw new ArgumentNullException ();
1204 if (startIndex < 0 || startIndex >= this.length)
1205 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1207 if (this.length == 0)
1210 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1213 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1216 throw new ArgumentNullException ();
1218 if ((startIndex < 0) || (startIndex >= this.Length))
1219 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1220 if ((count < 0) || (count > this.Length))
1221 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1222 if (startIndex - count + 1 < 0)
1223 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1225 if (this.length == 0)
1228 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1231 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1233 if (anyOf.Length == 1)
1234 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1236 fixed (char* start = this, testStart = anyOf) {
1237 char* ptr = start + startIndex;
1238 char* ptrEnd = ptr - count;
1240 char* testEnd = testStart + anyOf.Length;
1242 while (ptr != ptrEnd) {
1244 while (test != testEnd) {
1246 return (int)(ptr - start);
1255 // Following methods are culture-insensitive
1256 public int LastIndexOf (char value)
1258 if (this.length == 0)
1261 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1264 public int LastIndexOf (char value, int startIndex)
1266 return LastIndexOf (value, startIndex, startIndex + 1);
1269 public int LastIndexOf (char value, int startIndex, int count)
1271 if (startIndex == 0 && this.length == 0)
1274 // >= for char (> for string)
1275 if ((startIndex < 0) || (startIndex >= this.Length))
1276 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1277 if ((count < 0) || (count > this.Length))
1278 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1279 if (startIndex - count + 1 < 0)
1280 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1282 return LastIndexOfUnchecked (value, startIndex, count);
1285 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1287 // It helps JIT compiler to optimize comparison
1288 int value_32 = (int)value;
1290 fixed (char* start = &start_char) {
1291 char* ptr = start + startIndex;
1292 char* end_ptr = ptr - (count >> 3 << 3);
1294 while (ptr != end_ptr) {
1295 if (*ptr == value_32)
1296 return (int)(ptr - start);
1297 if (ptr[-1] == value_32)
1298 return (int)(ptr - start) - 1;
1299 if (ptr[-2] == value_32)
1300 return (int)(ptr - start) - 2;
1301 if (ptr[-3] == value_32)
1302 return (int)(ptr - start) - 3;
1303 if (ptr[-4] == value_32)
1304 return (int)(ptr - start) - 4;
1305 if (ptr[-5] == value_32)
1306 return (int)(ptr - start) - 5;
1307 if (ptr[-6] == value_32)
1308 return (int)(ptr - start) - 6;
1309 if (ptr[-7] == value_32)
1310 return (int)(ptr - start) - 7;
1315 end_ptr -= count & 0x07;
1316 while (ptr != end_ptr) {
1317 if (*ptr == value_32)
1318 return (int)(ptr - start);
1326 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1330 int end = startIndex - count;
1331 char c = Char.ToUpperInvariant (value);
1332 fixed (char* s = &start_char) {
1333 for (int i = startIndex; i > end; i--)
1334 if (Char.ToUpperInvariant (s [i]) == c)
1340 // Following methods are culture-sensitive
1341 public int LastIndexOf (String value)
1343 if (this.length == 0)
1344 // This overload does additional checking
1345 return LastIndexOf (value, 0, 0);
1347 return LastIndexOf (value, this.length - 1, this.length);
1350 public int LastIndexOf (String value, int startIndex)
1352 int max = startIndex;
1353 if (max < this.Length)
1355 return LastIndexOf (value, startIndex, max);
1358 public int LastIndexOf (String value, int startIndex, int count)
1362 throw new ArgumentNullException ("value");
1364 throw new ArgumentNullException ("string2");
1367 // -1 > startIndex > for string (0 > startIndex >= for char)
1368 if ((startIndex < -1) || (startIndex > this.Length))
1369 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1370 if ((count < 0) || (count > this.Length))
1371 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1372 if (startIndex - count + 1 < 0)
1373 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1375 if (value.Length == 0)
1378 if (startIndex == 0 && this.length == 0)
1381 // This check is needed to match undocumented MS behaviour
1382 if (this.length == 0 && value.length > 0)
1388 if (startIndex == this.Length)
1390 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1394 public bool Contains (String value)
1396 return IndexOf (value) != -1;
1399 public static bool IsNullOrEmpty (String value)
1401 return (value == null) || (value.Length == 0);
1404 public string Normalize ()
1406 return Normalization.Normalize (this, 0);
1409 public string Normalize (NormalizationForm form)
1413 return Normalization.Normalize (this, 0);
1414 case NormalizationForm.FormD:
1415 return Normalization.Normalize (this, 1);
1416 case NormalizationForm.FormKC:
1417 return Normalization.Normalize (this, 2);
1418 case NormalizationForm.FormKD:
1419 return Normalization.Normalize (this, 3);
1423 public bool IsNormalized ()
1425 return Normalization.IsNormalized (this, 0);
1428 public bool IsNormalized (NormalizationForm form)
1432 return Normalization.IsNormalized (this, 0);
1433 case NormalizationForm.FormD:
1434 return Normalization.IsNormalized (this, 1);
1435 case NormalizationForm.FormKC:
1436 return Normalization.IsNormalized (this, 2);
1437 case NormalizationForm.FormKD:
1438 return Normalization.IsNormalized (this, 3);
1442 public string Remove (int startIndex)
1445 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1446 if (startIndex >= this.length)
1447 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1449 return Remove (startIndex, this.length - startIndex);
1453 public String PadLeft (int totalWidth)
1455 return PadLeft (totalWidth, ' ');
1458 public unsafe String PadLeft (int totalWidth, char paddingChar)
1460 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1463 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1465 if (totalWidth < this.length)
1468 String tmp = InternalAllocateStr (totalWidth);
1470 fixed (char* dest = tmp, src = this) {
1471 char* padPos = dest;
1472 char* padTo = dest + (totalWidth - length);
1473 while (padPos != padTo)
1474 *padPos++ = paddingChar;
1476 CharCopy (padTo, src, length);
1481 public String PadRight (int totalWidth)
1483 return PadRight (totalWidth, ' ');
1486 public unsafe String PadRight (int totalWidth, char paddingChar)
1488 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1491 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1493 if (totalWidth < this.length)
1496 String tmp = InternalAllocateStr (totalWidth);
1498 fixed (char* dest = tmp, src = this) {
1499 CharCopy (dest, src, length);
1501 char* padPos = dest + length;
1502 char* padTo = dest + totalWidth;
1503 while (padPos != padTo)
1504 *padPos++ = paddingChar;
1509 public bool StartsWith (String value)
1512 throw new ArgumentNullException ("value");
1514 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1518 [ComVisible (false)]
1519 public bool StartsWith (string value, StringComparison comparisonType)
1522 throw new ArgumentNullException ("value");
1524 switch (comparisonType) {
1525 case StringComparison.CurrentCulture:
1526 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1527 case StringComparison.CurrentCultureIgnoreCase:
1528 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1529 case StringComparison.InvariantCulture:
1530 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1531 case StringComparison.InvariantCultureIgnoreCase:
1532 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1533 case StringComparison.Ordinal:
1534 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1535 case StringComparison.OrdinalIgnoreCase:
1536 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1538 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1539 throw new ArgumentException (msg, "comparisonType");
1543 [ComVisible (false)]
1544 public bool EndsWith (string value, StringComparison comparisonType)
1547 throw new ArgumentNullException ("value");
1549 switch (comparisonType) {
1550 case StringComparison.CurrentCulture:
1551 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1552 case StringComparison.CurrentCultureIgnoreCase:
1553 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1554 case StringComparison.InvariantCulture:
1555 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1556 case StringComparison.InvariantCultureIgnoreCase:
1557 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1558 case StringComparison.Ordinal:
1559 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1560 case StringComparison.OrdinalIgnoreCase:
1561 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1563 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1564 throw new ArgumentException (msg, "comparisonType");
1574 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1576 if (culture == null)
1577 culture = CultureInfo.CurrentCulture;
1579 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1582 /* This method is culture insensitive */
1583 public unsafe String Replace (char oldChar, char newChar)
1585 if (this.length == 0 || oldChar == newChar)
1588 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1589 if (start_pos == -1)
1595 string tmp = InternalAllocateStr(length);
1596 fixed (char* dest = tmp, src = &start_char) {
1598 memcpy((byte*)dest, (byte*)src, start_pos * 2);
1600 char* end_ptr = dest + length;
1601 char* dest_ptr = dest + start_pos;
1602 char* src_ptr = src + start_pos;
1604 while (dest_ptr != end_ptr) {
1605 if (*src_ptr == oldChar)
1606 *dest_ptr = newChar;
1608 *dest_ptr = *src_ptr;
1617 /* This method is culture sensitive */
1618 public String Replace (String oldValue, String newValue)
1620 if (oldValue == null)
1621 throw new ArgumentNullException ("oldValue");
1623 if (oldValue.Length == 0)
1624 throw new ArgumentException ("oldValue is the empty string.");
1626 if (this.Length == 0)
1629 if (newValue == null)
1630 newValue = String.Empty;
1632 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1635 public unsafe String Remove (int startIndex, int count)
1638 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1640 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1641 if (startIndex > this.length - count)
1642 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1644 String tmp = InternalAllocateStr (this.length - count);
1646 fixed (char *dest = tmp, src = this) {
1648 CharCopy (dst, src, startIndex);
1649 int skip = startIndex + count;
1651 CharCopy (dst, src + skip, length - skip);
1656 public String ToLower ()
1658 return ToLower (CultureInfo.CurrentCulture);
1661 public String ToLower (CultureInfo culture)
1663 if (culture == null)
1664 throw new ArgumentNullException ("culture");
1666 if (culture.LCID == 0x007F) // Invariant
1667 return ToLowerInvariant ();
1669 return culture.TextInfo.ToLower (this);
1673 public unsafe String ToLowerInvariant ()
1675 internal unsafe String ToLowerInvariant ()
1678 string tmp = InternalAllocateStr (length);
1679 fixed (char* source = &start_char, dest = tmp) {
1681 char* destPtr = (char*)dest;
1682 char* sourcePtr = (char*)source;
1684 for (int n = 0; n < length; n++) {
1685 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1693 public String ToUpper ()
1695 return ToUpper (CultureInfo.CurrentCulture);
1698 public String ToUpper (CultureInfo culture)
1700 if (culture == null)
1701 throw new ArgumentNullException ("culture");
1703 if (culture.LCID == 0x007F) // Invariant
1704 return ToUpperInvariant ();
1706 return culture.TextInfo.ToUpper (this);
1710 public unsafe String ToUpperInvariant ()
1712 internal unsafe String ToUpperInvariant ()
1715 string tmp = InternalAllocateStr (length);
1716 fixed (char* source = &start_char, dest = tmp) {
1718 char* destPtr = (char*)dest;
1719 char* sourcePtr = (char*)source;
1721 for (int n = 0; n < length; n++) {
1722 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1730 public override String ToString ()
1735 public String ToString (IFormatProvider provider)
1740 public static String Format (String format, Object arg0)
1742 return Format (null, format, new Object[] {arg0});
1745 public static String Format (String format, Object arg0, Object arg1)
1747 return Format (null, format, new Object[] {arg0, arg1});
1750 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1752 return Format (null, format, new Object[] {arg0, arg1, arg2});
1755 public static string Format (string format, params object[] args)
1757 return Format (null, format, args);
1760 public static string Format (IFormatProvider provider, string format, params object[] args)
1762 StringBuilder b = new StringBuilder ();
1763 FormatHelper (b, provider, format, args);
1764 return b.ToString ();
1767 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1770 throw new ArgumentNullException ("format");
1772 throw new ArgumentNullException ("args");
1776 while (ptr < format.length) {
1777 char c = format[ptr ++];
1780 result.Append (format, start, ptr - start - 1);
1782 // check for escaped open bracket
1784 if (format[ptr] == '{') {
1795 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1796 if (n >= args.Length)
1797 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1801 object arg = args[n];
1804 ICustomFormatter formatter = null;
1805 if (provider != null)
1806 formatter = provider.GetFormat (typeof (ICustomFormatter))
1807 as ICustomFormatter;
1810 else if (formatter != null)
1811 str = formatter.Format (arg_format, arg, provider);
1812 else if (arg is IFormattable)
1813 str = ((IFormattable)arg).ToString (arg_format, provider);
1815 str = arg.ToString ();
1817 // pad formatted string and append to result
1819 if (width > str.length) {
1820 const char padchar = ' ';
1821 int padlen = width - str.length;
1824 result.Append (str);
1825 result.Append (padchar, padlen);
1828 result.Append (padchar, padlen);
1829 result.Append (str);
1833 result.Append (str);
1837 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1838 result.Append (format, start, ptr - start - 1);
1841 else if (c == '}') {
1842 throw new FormatException ("Input string was not in a correct format.");
1846 if (start < format.length)
1847 result.Append (format, start, format.Length - start);
1850 public unsafe static String Copy (String str)
1853 throw new ArgumentNullException ("str");
1855 int length = str.length;
1857 String tmp = InternalAllocateStr (length);
1859 fixed (char *dest = tmp, src = str) {
1860 CharCopy (dest, src, length);
1866 public static String Concat (Object obj)
1869 return String.Empty;
1871 return obj.ToString ();
1874 public unsafe static String Concat (Object obj1, Object obj2)
1878 s1 = (obj1 != null) ? obj1.ToString () : null;
1879 s2 = (obj2 != null) ? obj2.ToString () : null;
1883 return String.Empty;
1886 } else if (s2 == null)
1889 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1890 if (s1.Length != 0) {
1891 fixed (char *dest = tmp, src = s1) {
1892 CharCopy (dest, src, s1.length);
1895 if (s2.Length != 0) {
1896 fixed (char *dest = tmp, src = s2) {
1897 CharCopy (dest + s1.Length, src, s2.length);
1904 public static String Concat (Object obj1, Object obj2, Object obj3)
1910 s1 = obj1.ToString ();
1915 s2 = obj2.ToString ();
1920 s3 = obj3.ToString ();
1922 return Concat (s1, s2, s3);
1925 #if ! BOOTSTRAP_WITH_OLDLIB
1926 [CLSCompliant(false)]
1927 public static String Concat (Object obj1, Object obj2, Object obj3,
1928 Object obj4, __arglist)
1930 string s1, s2, s3, s4;
1935 s1 = obj1.ToString ();
1940 s2 = obj2.ToString ();
1945 s3 = obj3.ToString ();
1947 ArgIterator iter = new ArgIterator (__arglist);
1948 int argCount = iter.GetRemainingCount();
1950 StringBuilder sb = new StringBuilder ();
1952 sb.Append (obj4.ToString ());
1954 for (int i = 0; i < argCount; i++) {
1955 TypedReference typedRef = iter.GetNextArg ();
1956 sb.Append (TypedReference.ToObject (typedRef));
1959 s4 = sb.ToString ();
1961 return Concat (s1, s2, s3, s4);
1965 public unsafe static String Concat (String s1, String s2)
1967 if (s1 == null || s1.Length == 0) {
1968 if (s2 == null || s2.Length == 0)
1969 return String.Empty;
1973 if (s2 == null || s2.Length == 0)
1976 String tmp = InternalAllocateStr (s1.length + s2.length);
1978 fixed (char *dest = tmp, src = s1)
1979 CharCopy (dest, src, s1.length);
1980 fixed (char *dest = tmp, src = s2)
1981 CharCopy (dest + s1.Length, src, s2.length);
1986 public unsafe static String Concat (String s1, String s2, String s3)
1988 if (s1 == null || s1.Length == 0){
1989 if (s2 == null || s2.Length == 0){
1990 if (s3 == null || s3.Length == 0)
1991 return String.Empty;
1994 if (s3 == null || s3.Length == 0)
1999 if (s2 == null || s2.Length == 0){
2000 if (s3 == null || s3.Length == 0)
2005 if (s3 == null || s3.Length == 0)
2010 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
2012 if (s1.Length != 0) {
2013 fixed (char *dest = tmp, src = s1) {
2014 CharCopy (dest, src, s1.length);
2017 if (s2.Length != 0) {
2018 fixed (char *dest = tmp, src = s2) {
2019 CharCopy (dest + s1.Length, src, s2.length);
2022 if (s3.Length != 0) {
2023 fixed (char *dest = tmp, src = s3) {
2024 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2031 public unsafe static String Concat (String s1, String s2, String s3, String s4)
2033 if (s1 == null && s2 == null && s3 == null && s4 == null)
2034 return String.Empty;
2045 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
2047 if (s1.Length != 0) {
2048 fixed (char *dest = tmp, src = s1) {
2049 CharCopy (dest, src, s1.length);
2052 if (s2.Length != 0) {
2053 fixed (char *dest = tmp, src = s2) {
2054 CharCopy (dest + s1.Length, src, s2.length);
2057 if (s3.Length != 0) {
2058 fixed (char *dest = tmp, src = s3) {
2059 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2062 if (s4.Length != 0) {
2063 fixed (char *dest = tmp, src = s4) {
2064 CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
2071 public static String Concat (params Object[] args)
2074 throw new ArgumentNullException ("args");
2076 int argLen = args.Length;
2078 return String.Empty;
2080 string [] strings = new string [argLen];
2082 for (int i = 0; i < argLen; i++) {
2083 if (args[i] != null) {
2084 strings[i] = args[i].ToString ();
2085 len += strings[i].length;
2089 return String.Empty;
2091 return ConcatInternal (strings, len);
2094 public static String Concat (params String[] values)
2097 throw new ArgumentNullException ("values");
2100 for (int i = 0; i < values.Length; i++) {
2101 String s = values[i];
2106 return String.Empty;
2108 return ConcatInternal (values, len);
2111 private static unsafe String ConcatInternal (String[] values, int length)
2113 String tmp = InternalAllocateStr (length);
2115 fixed (char* dest = tmp) {
2117 for (int i = 0; i < values.Length; i++) {
2118 String source = values[i];
2119 if (source != null) {
2120 fixed (char* src = source) {
2121 CharCopy (dest + pos, src, source.length);
2123 pos += source.Length;
2130 public unsafe String Insert (int startIndex, String value)
2133 throw new ArgumentNullException ("value");
2135 if (startIndex < 0 || startIndex > this.length)
2136 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2138 if (value.Length == 0)
2140 if (this.Length == 0)
2142 String tmp = InternalAllocateStr (this.length + value.length);
2144 fixed (char *dest = tmp, src = this, val = value) {
2146 CharCopy (dst, src, startIndex);
2148 CharCopy (dst, val, value.length);
2149 dst += value.length;
2150 CharCopy (dst, src + startIndex, length - startIndex);
2155 public static string Intern (string str)
2158 throw new ArgumentNullException ("str");
2160 return InternalIntern (str);
2163 public static string IsInterned (string str)
2166 throw new ArgumentNullException ("str");
2168 return InternalIsInterned (str);
2171 public static string Join (string separator, string [] value)
2174 throw new ArgumentNullException ("value");
2175 if (separator == null)
2176 separator = String.Empty;
2178 return JoinUnchecked (separator, value, 0, value.Length);
2181 public static string Join (string separator, string[] value, int startIndex, int count)
2184 throw new ArgumentNullException ("value");
2186 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2188 throw new ArgumentOutOfRangeException ("count", "< 0");
2189 if (startIndex > value.Length - count)
2190 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2192 if (startIndex == value.Length)
2193 return String.Empty;
2194 if (separator == null)
2195 separator = String.Empty;
2197 return JoinUnchecked (separator, value, startIndex, count);
2200 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2202 // Unchecked parameters
2203 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2204 // separator and value must not be null
2207 int maxIndex = startIndex + count;
2208 // Precount the number of characters that the resulting string will have
2209 for (int i = startIndex; i < maxIndex; i++) {
2210 String s = value[i];
2214 length += separator.length * (count - 1);
2216 return String.Empty;
2218 String tmp = InternalAllocateStr (length);
2221 fixed (char* dest = tmp, sepsrc = separator) {
2222 // Copy each string from value except the last one and add a separator for each
2224 for (int i = startIndex; i < maxIndex; i++) {
2225 String source = value[i];
2226 if (source != null) {
2227 if (source.Length > 0) {
2228 fixed (char* src = source)
2229 CharCopy (dest + pos, src, source.Length);
2230 pos += source.Length;
2233 if (separator.Length > 0) {
2234 CharCopy (dest + pos, sepsrc, separator.Length);
2235 pos += separator.Length;
2238 // Append last string that does not get an additional separator
2239 String sourceLast = value[maxIndex];
2240 if (sourceLast != null) {
2241 if (sourceLast.Length > 0) {
2242 fixed (char* src = sourceLast)
2243 CharCopy (dest + pos, src, sourceLast.Length);
2250 bool IConvertible.ToBoolean (IFormatProvider provider)
2252 return Convert.ToBoolean (this, provider);
2255 byte IConvertible.ToByte (IFormatProvider provider)
2257 return Convert.ToByte (this, provider);
2260 char IConvertible.ToChar (IFormatProvider provider)
2262 return Convert.ToChar (this, provider);
2265 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2267 return Convert.ToDateTime (this, provider);
2270 decimal IConvertible.ToDecimal (IFormatProvider provider)
2272 return Convert.ToDecimal (this, provider);
2275 double IConvertible.ToDouble (IFormatProvider provider)
2277 return Convert.ToDouble (this, provider);
2280 short IConvertible.ToInt16 (IFormatProvider provider)
2282 return Convert.ToInt16 (this, provider);
2285 int IConvertible.ToInt32 (IFormatProvider provider)
2287 return Convert.ToInt32 (this, provider);
2290 long IConvertible.ToInt64 (IFormatProvider provider)
2292 return Convert.ToInt64 (this, provider);
2295 sbyte IConvertible.ToSByte (IFormatProvider provider)
2297 return Convert.ToSByte (this, provider);
2300 float IConvertible.ToSingle (IFormatProvider provider)
2302 return Convert.ToSingle (this, provider);
2305 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2307 return Convert.ToType (this, conversionType, provider);
2310 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2312 return Convert.ToUInt16 (this, provider);
2315 uint IConvertible.ToUInt32 (IFormatProvider provider)
2317 return Convert.ToUInt32 (this, provider);
2320 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2322 return Convert.ToUInt64 (this, provider);
2331 public CharEnumerator GetEnumerator ()
2333 return new CharEnumerator (this);
2337 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2339 return new CharEnumerator (this);
2343 IEnumerator IEnumerable.GetEnumerator ()
2345 return new CharEnumerator (this);
2348 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2349 out bool left_align, out string format)
2351 // parses format specifier of form:
2357 // N = argument number (non-negative integer)
2359 n = ParseDecimal (str, ref ptr);
2361 throw new FormatException ("Input string was not in a correct format.");
2363 // M = width (non-negative integer)
2365 if (str[ptr] == ',') {
2366 // White space between ',' and number or sign.
2368 while (Char.IsWhiteSpace (str [ptr]))
2372 format = str.Substring (start, ptr - start);
2374 left_align = (str [ptr] == '-');
2378 width = ParseDecimal (str, ref ptr);
2380 throw new FormatException ("Input string was not in a correct format.");
2385 format = String.Empty;
2388 // F = argument format (string)
2390 if (str[ptr] == ':') {
2392 while (str[ptr] != '}')
2395 format += str.Substring (start, ptr - start);
2400 if (str[ptr ++] != '}')
2401 throw new FormatException ("Input string was not in a correct format.");
2403 catch (IndexOutOfRangeException) {
2404 throw new FormatException ("Input string was not in a correct format.");
2408 private static int ParseDecimal (string str, ref int ptr)
2414 if (c < '0' || '9' < c)
2417 n = n * 10 + c - '0';
2428 internal unsafe void InternalSetChar (int idx, char val)
2430 if ((uint) idx >= (uint) Length)
2431 throw new ArgumentOutOfRangeException ("idx");
2433 fixed (char * pStr = &start_char)
2439 internal unsafe void InternalSetLength (int newLength)
2441 if (newLength > length)
2442 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2444 // zero terminate, we can pass string objects directly via pinvoke
2445 // we also zero the rest of the string, since the new GC needs to be
2446 // able to handle the changing size (it will skip the 0 bytes).
2447 fixed (char * pStr = &start_char) {
2448 char *p = pStr + newLength;
2449 char *end = pStr + length;
2459 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2461 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2462 public unsafe override int GetHashCode ()
2464 fixed (char * c = this) {
2466 char * end = cc + length - 1;
2468 for (;cc < end; cc += 2) {
2469 h = (h << 5) - h + *cc;
2470 h = (h << 5) - h + cc [1];
2474 h = (h << 5) - h + *cc;
2479 internal unsafe int GetCaseInsensitiveHashCode ()
2481 fixed (char * c = this) {
2483 char * end = cc + length - 1;
2485 for (;cc < end; cc += 2) {
2486 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2487 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2491 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2496 // Certain constructors are redirected to CreateString methods with
2497 // matching argument list. The this pointer should not be used.
2499 private unsafe String CreateString (sbyte* value)
2502 return String.Empty;
2504 byte* bytes = (byte*) value;
2508 while (bytes++ [0] != 0)
2510 } catch (NullReferenceException) {
2511 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2513 } catch (AccessViolationException) {
2514 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2518 return CreateString (value, 0, length, null);
2521 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2523 return CreateString (value, startIndex, length, null);
2526 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2529 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2531 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2532 if (value + startIndex < value)
2533 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2535 bool isDefaultEncoding;
2537 if (isDefaultEncoding = (enc == null)) {
2540 throw new ArgumentNullException ("value");
2543 if (value == null || length == 0)
2545 return String.Empty;
2547 enc = Encoding.Default;
2550 byte [] bytes = new byte [length];
2553 fixed (byte* bytePtr = bytes)
2555 memcpy (bytePtr, (byte*) (value + startIndex), length);
2556 } catch (NullReferenceException) {
2558 if (!isDefaultEncoding)
2562 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2564 } catch (AccessViolationException) {
2565 if (!isDefaultEncoding)
2568 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2572 // GetString () is called even when length == 0
2573 return enc.GetString (bytes);
2576 unsafe string CreateString (char *value)
2579 return string.Empty;
2586 string result = InternalAllocateStr (i);
2589 fixed (char *dest = result) {
2590 CharCopy (dest, value, i);
2596 unsafe string CreateString (char *value, int startIndex, int length)
2599 return string.Empty;
2601 throw new ArgumentNullException ("value");
2603 throw new ArgumentOutOfRangeException ("startIndex");
2605 throw new ArgumentOutOfRangeException ("length");
2607 string result = InternalAllocateStr (length);
2609 fixed (char *dest = result) {
2610 CharCopy (dest, value + startIndex, length);
2615 unsafe string CreateString (char [] val, int startIndex, int length)
2618 throw new ArgumentNullException ("value");
2620 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2622 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2623 if (startIndex > val.Length - length)
2624 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2626 return string.Empty;
2628 string result = InternalAllocateStr (length);
2630 fixed (char *dest = result, src = val) {
2631 CharCopy (dest, src + startIndex, length);
2636 unsafe string CreateString (char [] val)
2639 return string.Empty;
2640 if (val.Length == 0)
2641 return string.Empty;
2642 string result = InternalAllocateStr (val.Length);
2644 fixed (char *dest = result, src = val) {
2645 CharCopy (dest, src, val.Length);
2650 unsafe string CreateString (char c, int count)
2653 throw new ArgumentOutOfRangeException ("count");
2655 return string.Empty;
2656 string result = InternalAllocateStr (count);
2657 fixed (char *dest = result) {
2659 char *end = p + count;
2668 /* helpers used by the runtime as well as above or eslewhere in corlib */
2669 internal static unsafe void memset (byte *dest, int val, int len)
2680 val = val | (val << 8);
2681 val = val | (val << 16);
2684 int rest = (int)dest & 3;
2692 } while (rest != 0);
2695 ((int*)dest) [0] = val;
2696 ((int*)dest) [1] = val;
2697 ((int*)dest) [2] = val;
2698 ((int*)dest) [3] = val;
2703 ((int*)dest) [0] = val;
2715 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2716 /*while (size >= 32) {
2717 // using long is better than int and slower than double
2718 // FIXME: enable this only on correct alignment or on platforms
2719 // that can tolerate unaligned reads/writes of doubles
2720 ((double*)dest) [0] = ((double*)src) [0];
2721 ((double*)dest) [1] = ((double*)src) [1];
2722 ((double*)dest) [2] = ((double*)src) [2];
2723 ((double*)dest) [3] = ((double*)src) [3];
2728 while (size >= 16) {
2729 ((int*)dest) [0] = ((int*)src) [0];
2730 ((int*)dest) [1] = ((int*)src) [1];
2731 ((int*)dest) [2] = ((int*)src) [2];
2732 ((int*)dest) [3] = ((int*)src) [3];
2738 ((int*)dest) [0] = ((int*)src) [0];
2744 ((byte*)dest) [0] = ((byte*)src) [0];
2750 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2752 ((short*)dest) [0] = ((short*)src) [0];
2753 ((short*)dest) [1] = ((short*)src) [1];
2754 ((short*)dest) [2] = ((short*)src) [2];
2755 ((short*)dest) [3] = ((short*)src) [3];
2761 ((short*)dest) [0] = ((short*)src) [0];
2767 ((byte*)dest) [0] = ((byte*)src) [0];
2769 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2771 ((byte*)dest) [0] = ((byte*)src) [0];
2772 ((byte*)dest) [1] = ((byte*)src) [1];
2773 ((byte*)dest) [2] = ((byte*)src) [2];
2774 ((byte*)dest) [3] = ((byte*)src) [3];
2775 ((byte*)dest) [4] = ((byte*)src) [4];
2776 ((byte*)dest) [5] = ((byte*)src) [5];
2777 ((byte*)dest) [6] = ((byte*)src) [6];
2778 ((byte*)dest) [7] = ((byte*)src) [7];
2784 ((byte*)dest) [0] = ((byte*)src) [0];
2785 ((byte*)dest) [1] = ((byte*)src) [1];
2791 ((byte*)dest) [0] = ((byte*)src) [0];
2794 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2795 // FIXME: if pointers are not aligned, try to align them
2796 // so a faster routine can be used. Handle the case where
2797 // the pointers can't be reduced to have the same alignment
2798 // (just ignore the issue on x86?)
2799 if ((((int)dest | (int)src) & 3) != 0) {
2800 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2806 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2807 ((short*)dest) [0] = ((short*)src) [0];
2812 if ((((int)dest | (int)src) & 1) != 0) {
2813 memcpy1 (dest, src, size);
2816 if ((((int)dest | (int)src) & 2) != 0) {
2817 memcpy2 (dest, src, size);
2821 memcpy4 (dest, src, size);
2824 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2825 // Same rules as for memcpy, but with the premise that
2826 // chars can only be aligned to even addresses if their
2827 // enclosing types are correctly aligned
2828 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2829 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2830 ((short*)dest) [0] = ((short*)src) [0];
2835 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2836 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2840 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2843 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2847 for (int i = count; i > 0; i--) {
2854 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2856 fixed (char* dest = target, src = source)
2857 CharCopy (dest + targetIndex, src + sourceIndex, count);
2860 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2862 fixed (char* dest = target, src = source)
2863 CharCopy (dest + targetIndex, src + sourceIndex, count);
2866 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2867 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2869 fixed (char* dest = target, src = source)
2870 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2873 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2874 unsafe public extern String (char *value);
2876 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2877 unsafe public extern String (char *value, int startIndex, int length);
2879 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2880 unsafe public extern String (sbyte *value);
2882 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2883 unsafe public extern String (sbyte *value, int startIndex, int length);
2885 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2886 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2888 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2889 public extern String (char [] val, int startIndex, int length);
2891 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2892 public extern String (char [] val);
2894 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2895 public extern String (char c, int count);
2897 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2898 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2900 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2901 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2903 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2904 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2906 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2907 private extern String[] InternalSplit (char[] separator, int count, int options);
2909 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2910 private extern String InternalTrim (char[] chars, int typ);
2912 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2913 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2915 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2916 private extern String InternalPad (int width, char chr, bool right);
2918 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2919 internal extern static String InternalAllocateStr (int length);
2921 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2922 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2924 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2925 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2927 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2928 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2930 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2931 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2933 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2934 private extern static string InternalIntern (string str);
2936 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2937 private extern static string InternalIsInterned (string str);