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 // Following 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 CharCopy (dest, src, start_pos);
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 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1618 public String Replace (String oldValue, String newValue)
1620 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1621 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1623 if (oldValue == null)
1624 throw new ArgumentNullException ("oldValue");
1626 if (oldValue.Length == 0)
1627 throw new ArgumentException ("oldValue is the empty string.");
1629 if (this.Length == 0)
1632 if (newValue == null)
1633 newValue = String.Empty;
1635 return ReplaceUnchecked (oldValue, newValue);
1638 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1640 if (oldValue.length > length)
1642 if (oldValue.length == 1 && newValue.length == 1) {
1643 return Replace (oldValue[0], newValue[0]);
1644 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1645 // because the length of the result would be this.length and length calculation unneccesary
1648 const int maxValue = 200; // Allocate 800 byte maximum
1649 int* dat = stackalloc int[maxValue];
1650 fixed (char* source = this, replace = newValue) {
1651 int i = 0, count = 0;
1652 while (i < length) {
1653 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1657 if (count < maxValue)
1658 dat[count++] = found;
1660 return ReplaceFallback (oldValue, newValue, maxValue);
1662 i = found + oldValue.length;
1664 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1665 String tmp = InternalAllocateStr (nlen);
1667 int curPos = 0, lastReadPos = 0;
1668 fixed (char* dest = tmp) {
1669 for (int j = 0; j < count; j++) {
1670 int precopy = dat[j] - lastReadPos;
1671 CharCopy (dest + curPos, source + lastReadPos, precopy);
1673 lastReadPos = dat[j] + oldValue.length;
1674 CharCopy (dest + curPos, replace, newValue.length);
1675 curPos += newValue.length;
1677 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1683 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1685 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1686 StringBuilder sb = new StringBuilder (lengthEstimate);
1687 for (int i = 0; i < length;) {
1688 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1690 sb.Append (SubstringUnchecked (i, length - i));
1693 sb.Append (SubstringUnchecked (i, found - i));
1694 sb.Append (newValue);
1695 i = found + oldValue.Length;
1697 return sb.ToString ();
1701 public unsafe String Remove (int startIndex, int count)
1704 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1706 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1707 if (startIndex > this.length - count)
1708 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1710 String tmp = InternalAllocateStr (this.length - count);
1712 fixed (char *dest = tmp, src = this) {
1714 CharCopy (dst, src, startIndex);
1715 int skip = startIndex + count;
1717 CharCopy (dst, src + skip, length - skip);
1722 public String ToLower ()
1724 return ToLower (CultureInfo.CurrentCulture);
1727 public String ToLower (CultureInfo culture)
1729 if (culture == null)
1730 throw new ArgumentNullException ("culture");
1732 if (culture.LCID == 0x007F) // Invariant
1733 return ToLowerInvariant ();
1735 return culture.TextInfo.ToLower (this);
1739 public unsafe String ToLowerInvariant ()
1741 internal unsafe String ToLowerInvariant ()
1744 string tmp = InternalAllocateStr (length);
1745 fixed (char* source = &start_char, dest = tmp) {
1747 char* destPtr = (char*)dest;
1748 char* sourcePtr = (char*)source;
1750 for (int n = 0; n < length; n++) {
1751 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1759 public String ToUpper ()
1761 return ToUpper (CultureInfo.CurrentCulture);
1764 public String ToUpper (CultureInfo culture)
1766 if (culture == null)
1767 throw new ArgumentNullException ("culture");
1769 if (culture.LCID == 0x007F) // Invariant
1770 return ToUpperInvariant ();
1772 return culture.TextInfo.ToUpper (this);
1776 public unsafe String ToUpperInvariant ()
1778 internal unsafe String ToUpperInvariant ()
1781 string tmp = InternalAllocateStr (length);
1782 fixed (char* source = &start_char, dest = tmp) {
1784 char* destPtr = (char*)dest;
1785 char* sourcePtr = (char*)source;
1787 for (int n = 0; n < length; n++) {
1788 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1796 public override String ToString ()
1801 public String ToString (IFormatProvider provider)
1806 public static String Format (String format, Object arg0)
1808 return Format (null, format, new Object[] {arg0});
1811 public static String Format (String format, Object arg0, Object arg1)
1813 return Format (null, format, new Object[] {arg0, arg1});
1816 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1818 return Format (null, format, new Object[] {arg0, arg1, arg2});
1821 public static string Format (string format, params object[] args)
1823 return Format (null, format, args);
1826 public static string Format (IFormatProvider provider, string format, params object[] args)
1828 StringBuilder b = new StringBuilder ();
1829 FormatHelper (b, provider, format, args);
1830 return b.ToString ();
1833 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1836 throw new ArgumentNullException ("format");
1838 throw new ArgumentNullException ("args");
1842 while (ptr < format.length) {
1843 char c = format[ptr ++];
1846 result.Append (format, start, ptr - start - 1);
1848 // check for escaped open bracket
1850 if (format[ptr] == '{') {
1861 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1862 if (n >= args.Length)
1863 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1867 object arg = args[n];
1870 ICustomFormatter formatter = null;
1871 if (provider != null)
1872 formatter = provider.GetFormat (typeof (ICustomFormatter))
1873 as ICustomFormatter;
1876 else if (formatter != null)
1877 str = formatter.Format (arg_format, arg, provider);
1878 else if (arg is IFormattable)
1879 str = ((IFormattable)arg).ToString (arg_format, provider);
1881 str = arg.ToString ();
1883 // pad formatted string and append to result
1885 if (width > str.length) {
1886 const char padchar = ' ';
1887 int padlen = width - str.length;
1890 result.Append (str);
1891 result.Append (padchar, padlen);
1894 result.Append (padchar, padlen);
1895 result.Append (str);
1899 result.Append (str);
1903 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1904 result.Append (format, start, ptr - start - 1);
1907 else if (c == '}') {
1908 throw new FormatException ("Input string was not in a correct format.");
1912 if (start < format.length)
1913 result.Append (format, start, format.Length - start);
1916 public unsafe static String Copy (String str)
1919 throw new ArgumentNullException ("str");
1921 int length = str.length;
1923 String tmp = InternalAllocateStr (length);
1925 fixed (char *dest = tmp, src = str) {
1926 CharCopy (dest, src, length);
1932 public static String Concat (Object obj)
1935 return String.Empty;
1937 return obj.ToString ();
1940 public unsafe static String Concat (Object obj1, Object obj2)
1944 s1 = (obj1 != null) ? obj1.ToString () : null;
1945 s2 = (obj2 != null) ? obj2.ToString () : null;
1949 return String.Empty;
1952 } else if (s2 == null)
1955 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1956 if (s1.Length != 0) {
1957 fixed (char *dest = tmp, src = s1) {
1958 CharCopy (dest, src, s1.length);
1961 if (s2.Length != 0) {
1962 fixed (char *dest = tmp, src = s2) {
1963 CharCopy (dest + s1.Length, src, s2.length);
1970 public static String Concat (Object obj1, Object obj2, Object obj3)
1976 s1 = obj1.ToString ();
1981 s2 = obj2.ToString ();
1986 s3 = obj3.ToString ();
1988 return Concat (s1, s2, s3);
1991 #if ! BOOTSTRAP_WITH_OLDLIB
1992 [CLSCompliant(false)]
1993 public static String Concat (Object obj1, Object obj2, Object obj3,
1994 Object obj4, __arglist)
1996 string s1, s2, s3, s4;
2001 s1 = obj1.ToString ();
2006 s2 = obj2.ToString ();
2011 s3 = obj3.ToString ();
2013 ArgIterator iter = new ArgIterator (__arglist);
2014 int argCount = iter.GetRemainingCount();
2016 StringBuilder sb = new StringBuilder ();
2018 sb.Append (obj4.ToString ());
2020 for (int i = 0; i < argCount; i++) {
2021 TypedReference typedRef = iter.GetNextArg ();
2022 sb.Append (TypedReference.ToObject (typedRef));
2025 s4 = sb.ToString ();
2027 return Concat (s1, s2, s3, s4);
2031 public unsafe static String Concat (String s1, String s2)
2033 if (s1 == null || s1.Length == 0) {
2034 if (s2 == null || s2.Length == 0)
2035 return String.Empty;
2039 if (s2 == null || s2.Length == 0)
2042 String tmp = InternalAllocateStr (s1.length + s2.length);
2044 fixed (char *dest = tmp, src = s1)
2045 CharCopy (dest, src, s1.length);
2046 fixed (char *dest = tmp, src = s2)
2047 CharCopy (dest + s1.Length, src, s2.length);
2052 public unsafe static String Concat (String s1, String s2, String s3)
2054 if (s1 == null || s1.Length == 0){
2055 if (s2 == null || s2.Length == 0){
2056 if (s3 == null || s3.Length == 0)
2057 return String.Empty;
2060 if (s3 == null || s3.Length == 0)
2065 if (s2 == null || s2.Length == 0){
2066 if (s3 == null || s3.Length == 0)
2071 if (s3 == null || s3.Length == 0)
2076 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
2078 if (s1.Length != 0) {
2079 fixed (char *dest = tmp, src = s1) {
2080 CharCopy (dest, src, s1.length);
2083 if (s2.Length != 0) {
2084 fixed (char *dest = tmp, src = s2) {
2085 CharCopy (dest + s1.Length, src, s2.length);
2088 if (s3.Length != 0) {
2089 fixed (char *dest = tmp, src = s3) {
2090 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2097 public unsafe static String Concat (String s1, String s2, String s3, String s4)
2099 if (s1 == null && s2 == null && s3 == null && s4 == null)
2100 return String.Empty;
2111 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
2113 if (s1.Length != 0) {
2114 fixed (char *dest = tmp, src = s1) {
2115 CharCopy (dest, src, s1.length);
2118 if (s2.Length != 0) {
2119 fixed (char *dest = tmp, src = s2) {
2120 CharCopy (dest + s1.Length, src, s2.length);
2123 if (s3.Length != 0) {
2124 fixed (char *dest = tmp, src = s3) {
2125 CharCopy (dest + s1.Length + s2.Length, src, s3.length);
2128 if (s4.Length != 0) {
2129 fixed (char *dest = tmp, src = s4) {
2130 CharCopy (dest + s1.Length + s2.Length + s3.Length, src, s4.length);
2137 public static String Concat (params Object[] args)
2140 throw new ArgumentNullException ("args");
2142 int argLen = args.Length;
2144 return String.Empty;
2146 string [] strings = new string [argLen];
2148 for (int i = 0; i < argLen; i++) {
2149 if (args[i] != null) {
2150 strings[i] = args[i].ToString ();
2151 len += strings[i].length;
2155 return String.Empty;
2157 return ConcatInternal (strings, len);
2160 public static String Concat (params String[] values)
2163 throw new ArgumentNullException ("values");
2166 for (int i = 0; i < values.Length; i++) {
2167 String s = values[i];
2172 return String.Empty;
2174 return ConcatInternal (values, len);
2177 private static unsafe String ConcatInternal (String[] values, int length)
2179 String tmp = InternalAllocateStr (length);
2181 fixed (char* dest = tmp) {
2183 for (int i = 0; i < values.Length; i++) {
2184 String source = values[i];
2185 if (source != null) {
2186 fixed (char* src = source) {
2187 CharCopy (dest + pos, src, source.length);
2189 pos += source.Length;
2196 public unsafe String Insert (int startIndex, String value)
2199 throw new ArgumentNullException ("value");
2201 if (startIndex < 0 || startIndex > this.length)
2202 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2204 if (value.Length == 0)
2206 if (this.Length == 0)
2208 String tmp = InternalAllocateStr (this.length + value.length);
2210 fixed (char *dest = tmp, src = this, val = value) {
2212 CharCopy (dst, src, startIndex);
2214 CharCopy (dst, val, value.length);
2215 dst += value.length;
2216 CharCopy (dst, src + startIndex, length - startIndex);
2221 public static string Intern (string str)
2224 throw new ArgumentNullException ("str");
2226 return InternalIntern (str);
2229 public static string IsInterned (string str)
2232 throw new ArgumentNullException ("str");
2234 return InternalIsInterned (str);
2237 public static string Join (string separator, string [] value)
2240 throw new ArgumentNullException ("value");
2241 if (separator == null)
2242 separator = String.Empty;
2244 return JoinUnchecked (separator, value, 0, value.Length);
2247 public static string Join (string separator, string[] value, int startIndex, int count)
2250 throw new ArgumentNullException ("value");
2252 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2254 throw new ArgumentOutOfRangeException ("count", "< 0");
2255 if (startIndex > value.Length - count)
2256 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2258 if (startIndex == value.Length)
2259 return String.Empty;
2260 if (separator == null)
2261 separator = String.Empty;
2263 return JoinUnchecked (separator, value, startIndex, count);
2266 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2268 // Unchecked parameters
2269 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2270 // separator and value must not be null
2273 int maxIndex = startIndex + count;
2274 // Precount the number of characters that the resulting string will have
2275 for (int i = startIndex; i < maxIndex; i++) {
2276 String s = value[i];
2280 length += separator.length * (count - 1);
2282 return String.Empty;
2284 String tmp = InternalAllocateStr (length);
2287 fixed (char* dest = tmp, sepsrc = separator) {
2288 // Copy each string from value except the last one and add a separator for each
2290 for (int i = startIndex; i < maxIndex; i++) {
2291 String source = value[i];
2292 if (source != null) {
2293 if (source.Length > 0) {
2294 fixed (char* src = source)
2295 CharCopy (dest + pos, src, source.Length);
2296 pos += source.Length;
2299 if (separator.Length > 0) {
2300 CharCopy (dest + pos, sepsrc, separator.Length);
2301 pos += separator.Length;
2304 // Append last string that does not get an additional separator
2305 String sourceLast = value[maxIndex];
2306 if (sourceLast != null) {
2307 if (sourceLast.Length > 0) {
2308 fixed (char* src = sourceLast)
2309 CharCopy (dest + pos, src, sourceLast.Length);
2316 bool IConvertible.ToBoolean (IFormatProvider provider)
2318 return Convert.ToBoolean (this, provider);
2321 byte IConvertible.ToByte (IFormatProvider provider)
2323 return Convert.ToByte (this, provider);
2326 char IConvertible.ToChar (IFormatProvider provider)
2328 return Convert.ToChar (this, provider);
2331 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2333 return Convert.ToDateTime (this, provider);
2336 decimal IConvertible.ToDecimal (IFormatProvider provider)
2338 return Convert.ToDecimal (this, provider);
2341 double IConvertible.ToDouble (IFormatProvider provider)
2343 return Convert.ToDouble (this, provider);
2346 short IConvertible.ToInt16 (IFormatProvider provider)
2348 return Convert.ToInt16 (this, provider);
2351 int IConvertible.ToInt32 (IFormatProvider provider)
2353 return Convert.ToInt32 (this, provider);
2356 long IConvertible.ToInt64 (IFormatProvider provider)
2358 return Convert.ToInt64 (this, provider);
2361 sbyte IConvertible.ToSByte (IFormatProvider provider)
2363 return Convert.ToSByte (this, provider);
2366 float IConvertible.ToSingle (IFormatProvider provider)
2368 return Convert.ToSingle (this, provider);
2371 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
2373 return Convert.ToType (this, conversionType, provider);
2376 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2378 return Convert.ToUInt16 (this, provider);
2381 uint IConvertible.ToUInt32 (IFormatProvider provider)
2383 return Convert.ToUInt32 (this, provider);
2386 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2388 return Convert.ToUInt64 (this, provider);
2397 public CharEnumerator GetEnumerator ()
2399 return new CharEnumerator (this);
2403 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2405 return new CharEnumerator (this);
2409 IEnumerator IEnumerable.GetEnumerator ()
2411 return new CharEnumerator (this);
2414 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2415 out bool left_align, out string format)
2417 // parses format specifier of form:
2423 // N = argument number (non-negative integer)
2425 n = ParseDecimal (str, ref ptr);
2427 throw new FormatException ("Input string was not in a correct format.");
2429 // M = width (non-negative integer)
2431 if (str[ptr] == ',') {
2432 // White space between ',' and number or sign.
2434 while (Char.IsWhiteSpace (str [ptr]))
2438 format = str.Substring (start, ptr - start);
2440 left_align = (str [ptr] == '-');
2444 width = ParseDecimal (str, ref ptr);
2446 throw new FormatException ("Input string was not in a correct format.");
2451 format = String.Empty;
2454 // F = argument format (string)
2456 if (str[ptr] == ':') {
2458 while (str[ptr] != '}')
2461 format += str.Substring (start, ptr - start);
2466 if (str[ptr ++] != '}')
2467 throw new FormatException ("Input string was not in a correct format.");
2469 catch (IndexOutOfRangeException) {
2470 throw new FormatException ("Input string was not in a correct format.");
2474 private static int ParseDecimal (string str, ref int ptr)
2480 if (c < '0' || '9' < c)
2483 n = n * 10 + c - '0';
2494 internal unsafe void InternalSetChar (int idx, char val)
2496 if ((uint) idx >= (uint) Length)
2497 throw new ArgumentOutOfRangeException ("idx");
2499 fixed (char * pStr = &start_char)
2505 internal unsafe void InternalSetLength (int newLength)
2507 if (newLength > length)
2508 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2510 // zero terminate, we can pass string objects directly via pinvoke
2511 // we also zero the rest of the string, since the new GC needs to be
2512 // able to handle the changing size (it will skip the 0 bytes).
2513 fixed (char * pStr = &start_char) {
2514 char *p = pStr + newLength;
2515 char *end = pStr + length;
2525 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2527 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2528 public unsafe override int GetHashCode ()
2530 fixed (char * c = this) {
2532 char * end = cc + length - 1;
2534 for (;cc < end; cc += 2) {
2535 h = (h << 5) - h + *cc;
2536 h = (h << 5) - h + cc [1];
2540 h = (h << 5) - h + *cc;
2545 internal unsafe int GetCaseInsensitiveHashCode ()
2547 fixed (char * c = this) {
2549 char * end = cc + length - 1;
2551 for (;cc < end; cc += 2) {
2552 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2553 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2557 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2562 // Certain constructors are redirected to CreateString methods with
2563 // matching argument list. The this pointer should not be used.
2565 private unsafe String CreateString (sbyte* value)
2568 return String.Empty;
2570 byte* bytes = (byte*) value;
2574 while (bytes++ [0] != 0)
2576 } catch (NullReferenceException) {
2577 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2579 } catch (AccessViolationException) {
2580 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2584 return CreateString (value, 0, length, null);
2587 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2589 return CreateString (value, startIndex, length, null);
2592 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2595 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2597 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2598 if (value + startIndex < value)
2599 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2601 bool isDefaultEncoding;
2603 if (isDefaultEncoding = (enc == null)) {
2606 throw new ArgumentNullException ("value");
2609 if (value == null || length == 0)
2611 return String.Empty;
2613 enc = Encoding.Default;
2616 byte [] bytes = new byte [length];
2619 fixed (byte* bytePtr = bytes)
2621 memcpy (bytePtr, (byte*) (value + startIndex), length);
2622 } catch (NullReferenceException) {
2624 if (!isDefaultEncoding)
2628 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2630 } catch (AccessViolationException) {
2631 if (!isDefaultEncoding)
2634 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2638 // GetString () is called even when length == 0
2639 return enc.GetString (bytes);
2642 unsafe string CreateString (char *value)
2645 return string.Empty;
2652 string result = InternalAllocateStr (i);
2655 fixed (char *dest = result) {
2656 CharCopy (dest, value, i);
2662 unsafe string CreateString (char *value, int startIndex, int length)
2665 return string.Empty;
2667 throw new ArgumentNullException ("value");
2669 throw new ArgumentOutOfRangeException ("startIndex");
2671 throw new ArgumentOutOfRangeException ("length");
2673 string result = InternalAllocateStr (length);
2675 fixed (char *dest = result) {
2676 CharCopy (dest, value + startIndex, length);
2681 unsafe string CreateString (char [] val, int startIndex, int length)
2684 throw new ArgumentNullException ("value");
2686 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2688 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2689 if (startIndex > val.Length - length)
2690 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2692 return string.Empty;
2694 string result = InternalAllocateStr (length);
2696 fixed (char *dest = result, src = val) {
2697 CharCopy (dest, src + startIndex, length);
2702 unsafe string CreateString (char [] val)
2705 return string.Empty;
2706 if (val.Length == 0)
2707 return string.Empty;
2708 string result = InternalAllocateStr (val.Length);
2710 fixed (char *dest = result, src = val) {
2711 CharCopy (dest, src, val.Length);
2716 unsafe string CreateString (char c, int count)
2719 throw new ArgumentOutOfRangeException ("count");
2721 return string.Empty;
2722 string result = InternalAllocateStr (count);
2723 fixed (char *dest = result) {
2725 char *end = p + count;
2734 /* helpers used by the runtime as well as above or eslewhere in corlib */
2735 internal static unsafe void memset (byte *dest, int val, int len)
2746 val = val | (val << 8);
2747 val = val | (val << 16);
2750 int rest = (int)dest & 3;
2758 } while (rest != 0);
2761 ((int*)dest) [0] = val;
2762 ((int*)dest) [1] = val;
2763 ((int*)dest) [2] = val;
2764 ((int*)dest) [3] = val;
2769 ((int*)dest) [0] = val;
2781 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2782 /*while (size >= 32) {
2783 // using long is better than int and slower than double
2784 // FIXME: enable this only on correct alignment or on platforms
2785 // that can tolerate unaligned reads/writes of doubles
2786 ((double*)dest) [0] = ((double*)src) [0];
2787 ((double*)dest) [1] = ((double*)src) [1];
2788 ((double*)dest) [2] = ((double*)src) [2];
2789 ((double*)dest) [3] = ((double*)src) [3];
2794 while (size >= 16) {
2795 ((int*)dest) [0] = ((int*)src) [0];
2796 ((int*)dest) [1] = ((int*)src) [1];
2797 ((int*)dest) [2] = ((int*)src) [2];
2798 ((int*)dest) [3] = ((int*)src) [3];
2804 ((int*)dest) [0] = ((int*)src) [0];
2810 ((byte*)dest) [0] = ((byte*)src) [0];
2816 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2818 ((short*)dest) [0] = ((short*)src) [0];
2819 ((short*)dest) [1] = ((short*)src) [1];
2820 ((short*)dest) [2] = ((short*)src) [2];
2821 ((short*)dest) [3] = ((short*)src) [3];
2827 ((short*)dest) [0] = ((short*)src) [0];
2833 ((byte*)dest) [0] = ((byte*)src) [0];
2835 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2837 ((byte*)dest) [0] = ((byte*)src) [0];
2838 ((byte*)dest) [1] = ((byte*)src) [1];
2839 ((byte*)dest) [2] = ((byte*)src) [2];
2840 ((byte*)dest) [3] = ((byte*)src) [3];
2841 ((byte*)dest) [4] = ((byte*)src) [4];
2842 ((byte*)dest) [5] = ((byte*)src) [5];
2843 ((byte*)dest) [6] = ((byte*)src) [6];
2844 ((byte*)dest) [7] = ((byte*)src) [7];
2850 ((byte*)dest) [0] = ((byte*)src) [0];
2851 ((byte*)dest) [1] = ((byte*)src) [1];
2857 ((byte*)dest) [0] = ((byte*)src) [0];
2860 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2861 // FIXME: if pointers are not aligned, try to align them
2862 // so a faster routine can be used. Handle the case where
2863 // the pointers can't be reduced to have the same alignment
2864 // (just ignore the issue on x86?)
2865 if ((((int)dest | (int)src) & 3) != 0) {
2866 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2872 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2873 ((short*)dest) [0] = ((short*)src) [0];
2878 if ((((int)dest | (int)src) & 1) != 0) {
2879 memcpy1 (dest, src, size);
2882 if ((((int)dest | (int)src) & 2) != 0) {
2883 memcpy2 (dest, src, size);
2887 memcpy4 (dest, src, size);
2890 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2891 // Same rules as for memcpy, but with the premise that
2892 // chars can only be aligned to even addresses if their
2893 // enclosing types are correctly aligned
2894 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2895 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2896 ((short*)dest) [0] = ((short*)src) [0];
2901 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2902 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2906 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2909 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2913 for (int i = count; i > 0; i--) {
2920 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2922 fixed (char* dest = target, src = source)
2923 CharCopy (dest + targetIndex, src + sourceIndex, count);
2926 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2928 fixed (char* dest = target, src = source)
2929 CharCopy (dest + targetIndex, src + sourceIndex, count);
2932 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2933 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2935 fixed (char* dest = target, src = source)
2936 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2939 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2940 unsafe public extern String (char *value);
2942 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2943 unsafe public extern String (char *value, int startIndex, int length);
2945 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2946 unsafe public extern String (sbyte *value);
2948 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2949 unsafe public extern String (sbyte *value, int startIndex, int length);
2951 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2952 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2954 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2955 public extern String (char [] val, int startIndex, int length);
2957 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2958 public extern String (char [] val);
2960 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2961 public extern String (char c, int count);
2963 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2964 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2966 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2967 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2969 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2970 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2972 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2973 private extern String[] InternalSplit (char[] separator, int count, int options);
2975 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2976 private extern String InternalTrim (char[] chars, int typ);
2978 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2979 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2981 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2982 private extern String InternalPad (int width, char chr, bool right);
2984 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2985 internal extern static String InternalAllocateStr (int length);
2987 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2988 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2990 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2991 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2993 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2994 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2996 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2997 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2999 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3000 private extern static string InternalIntern (string str);
3002 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3003 private extern static string InternalIsInterned (string str);