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 static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
641 throw new ArgumentNullException ("culture");
643 return culture.CompareInfo.Compare (strA, strB, options);
646 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
649 throw new ArgumentNullException ("culture");
654 if (length > (strA.Length - indexA))
655 len1 = strA.Length - indexA;
657 if (length > (strB.Length - indexB))
658 len2 = strB.Length - indexB;
660 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
664 public int CompareTo (Object value)
669 if (!(value is String))
670 throw new ArgumentException ();
672 return String.Compare (this, (String) value);
675 public int CompareTo (String strB)
680 return Compare (this, strB);
683 public static int CompareOrdinal (String strA, String strB)
685 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
688 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
690 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
691 throw new ArgumentOutOfRangeException ();
693 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
696 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
698 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
699 throw new ArgumentOutOfRangeException ();
701 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
704 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
711 } else if (strB == null) {
714 int lengthA = Math.Min (lenA, strA.Length - indexA);
715 int lengthB = Math.Min (lenB, strB.Length - indexB);
717 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
720 fixed (char* aptr = strA, bptr = strB) {
721 char* ap = aptr + indexA;
722 char* end = ap + Math.Min (lengthA, lengthB);
723 char* bp = bptr + indexB;
730 return lengthA - lengthB;
734 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
736 // Same as above, but checks versus uppercase characters
742 } else if (strB == null) {
745 int lengthA = Math.Min (lenA, strA.Length - indexA);
746 int lengthB = Math.Min (lenB, strB.Length - indexB);
748 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
751 fixed (char* aptr = strA, bptr = strB) {
752 char* ap = aptr + indexA;
753 char* end = ap + Math.Min (lengthA, lengthB);
754 char* bp = bptr + indexB;
757 char c1 = Char.ToUpperInvariant (*ap);
758 char c2 = Char.ToUpperInvariant (*bp);
765 return lengthA - lengthB;
769 public bool EndsWith (String value)
772 throw new ArgumentNullException ("value");
774 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
782 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
785 throw new ArgumentNullException ("value");
787 culture = CultureInfo.CurrentCulture;
789 return culture.CompareInfo.IsSuffix (this, value,
790 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
793 // Following methods are culture-insensitive
794 public int IndexOfAny (char [] anyOf)
797 throw new ArgumentNullException ();
798 if (this.length == 0)
801 return IndexOfAnyUnchecked (anyOf, 0, this.length);
804 public int IndexOfAny (char [] anyOf, int startIndex)
807 throw new ArgumentNullException ();
808 if (startIndex < 0 || startIndex > this.length)
809 throw new ArgumentOutOfRangeException ();
811 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
814 public int IndexOfAny (char [] anyOf, int startIndex, int count)
817 throw new ArgumentNullException ();
818 if (startIndex < 0 || startIndex > this.length)
819 throw new ArgumentOutOfRangeException ();
820 if (count < 0 || startIndex > this.length - count)
821 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
823 return IndexOfAnyUnchecked (anyOf, startIndex, count);
826 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
828 if (anyOf.Length == 0)
831 if (anyOf.Length == 1)
832 return IndexOfUnchecked (anyOf[0], startIndex, count);
834 fixed (char* any = anyOf) {
838 char* end_any_ptr = any + anyOf.Length;
840 while (++any_ptr != end_any_ptr) {
841 if (*any_ptr > highest) {
846 if (*any_ptr < lowest)
850 fixed (char* start = &start_char) {
851 char* ptr = start + startIndex;
852 char* end_ptr = ptr + count;
854 while (ptr != end_ptr) {
855 if (*ptr > highest || *ptr < lowest) {
861 return (int)(ptr - start);
864 while (++any_ptr != end_any_ptr) {
865 if (*ptr == *any_ptr)
866 return (int)(ptr - start);
878 public int IndexOf (string value, StringComparison comparisonType)
880 return IndexOf (value, 0, this.Length, comparisonType);
883 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
885 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
888 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
890 switch (comparisonType) {
891 case StringComparison.CurrentCulture:
892 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
893 case StringComparison.CurrentCultureIgnoreCase:
894 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
895 case StringComparison.InvariantCulture:
896 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
897 case StringComparison.InvariantCultureIgnoreCase:
898 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
899 case StringComparison.Ordinal:
900 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
901 case StringComparison.OrdinalIgnoreCase:
902 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
904 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
905 throw new ArgumentException (msg, "comparisonType");
910 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
913 throw new ArgumentNullException ("value");
915 throw new ArgumentOutOfRangeException ("startIndex");
916 if (count < 0 || (this.length - startIndex) < count)
917 throw new ArgumentOutOfRangeException ("count");
919 if (options == CompareOptions.Ordinal)
920 return IndexOfOrdinalUnchecked (value, startIndex, count);
921 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
924 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
926 int valueLen = value.Length;
927 if (count < valueLen)
932 return IndexOfUnchecked (value[0], startIndex, count);
936 fixed (char* thisptr = this, valueptr = value) {
937 char* ap = thisptr + startIndex;
938 char* thisEnd = ap + count - valueLen + 1;
939 while (ap != thisEnd) {
940 if (*ap == *valueptr) {
941 for (int i = 1; i < valueLen; i++) {
942 if (ap[i] != valueptr[i])
945 return (int)(ap - thisptr);
954 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
956 int valueLen = value.Length;
957 if (count < valueLen)
963 fixed (char* thisptr = this, valueptr = value) {
964 char* ap = thisptr + startIndex;
965 char* thisEnd = ap + count - valueLen + 1;
966 while (ap != thisEnd) {
967 for (int i = 0; i < valueLen; i++) {
968 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
971 return (int)(ap - thisptr);
981 public int LastIndexOf (string value, StringComparison comparisonType)
983 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
986 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
988 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
991 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
993 switch (comparisonType) {
994 case StringComparison.CurrentCulture:
995 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
996 case StringComparison.CurrentCultureIgnoreCase:
997 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
998 case StringComparison.InvariantCulture:
999 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1000 case StringComparison.InvariantCultureIgnoreCase:
1001 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1002 case StringComparison.Ordinal:
1003 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1004 case StringComparison.OrdinalIgnoreCase:
1005 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1007 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1008 throw new ArgumentException (msg, "comparisonType");
1013 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1016 throw new ArgumentNullException ("value");
1017 if (startIndex < 0 || startIndex > length)
1018 throw new ArgumentOutOfRangeException ("startIndex");
1019 if (count < 0 || (startIndex < count - 1))
1020 throw new ArgumentOutOfRangeException ("count");
1022 if (options == CompareOptions.Ordinal)
1023 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1024 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1027 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1029 int valueLen = value.Length;
1030 if (count < valueLen)
1033 if (valueLen <= 1) {
1035 return LastIndexOfUnchecked (value[0], startIndex, count);
1039 fixed (char* thisptr = this, valueptr = value) {
1040 char* ap = thisptr + startIndex - valueLen + 1;
1041 char* thisEnd = ap - count + valueLen - 1;
1042 while (ap != thisEnd) {
1043 if (*ap == *valueptr) {
1044 for (int i = 1; i < valueLen; i++) {
1045 if (ap[i] != valueptr[i])
1048 return (int)(ap - thisptr);
1057 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1059 int valueLen = value.Length;
1060 if (count < valueLen)
1066 fixed (char* thisptr = this, valueptr = value) {
1067 char* ap = thisptr + startIndex - valueLen + 1;
1068 char* thisEnd = ap - count + valueLen - 1;
1069 while (ap != thisEnd) {
1070 for (int i = 0; i < valueLen; i++) {
1071 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1074 return (int)(ap - thisptr);
1082 // Following methods are culture-insensitive
1083 public int IndexOf (char value)
1085 if (this.length == 0)
1088 return IndexOfUnchecked (value, 0, this.length);
1091 public int IndexOf (char value, int startIndex)
1094 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1095 if (startIndex > this.length)
1096 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1098 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1101 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1104 public int IndexOf (char value, int startIndex, int count)
1106 if (startIndex < 0 || startIndex > this.length)
1107 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1109 throw new ArgumentOutOfRangeException ("count", "< 0");
1110 if (startIndex > this.length - count)
1111 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1113 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1116 return IndexOfUnchecked (value, startIndex, count);
1119 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1121 // It helps JIT compiler to optimize comparison
1122 int value_32 = (int)value;
1124 fixed (char* start = &start_char) {
1125 char* ptr = start + startIndex;
1126 char* end_ptr = ptr + (count >> 3 << 3);
1128 while (ptr != end_ptr) {
1129 if (*ptr == value_32)
1130 return (int)(ptr - start);
1131 if (ptr[1] == value_32)
1132 return (int)(ptr - start + 1);
1133 if (ptr[2] == value_32)
1134 return (int)(ptr - start + 2);
1135 if (ptr[3] == value_32)
1136 return (int)(ptr - start + 3);
1137 if (ptr[4] == value_32)
1138 return (int)(ptr - start + 4);
1139 if (ptr[5] == value_32)
1140 return (int)(ptr - start + 5);
1141 if (ptr[6] == value_32)
1142 return (int)(ptr - start + 6);
1143 if (ptr[7] == value_32)
1144 return (int)(ptr - start + 7);
1149 end_ptr += count & 0x07;
1150 while (ptr != end_ptr) {
1151 if (*ptr == value_32)
1152 return (int)(ptr - start);
1160 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1164 int end = startIndex + count;
1165 char c = Char.ToUpperInvariant (value);
1166 fixed (char* s = &start_char) {
1167 for (int i = startIndex; i < end; i++)
1168 if (Char.ToUpperInvariant (s [i]) == c)
1174 // Following methods are culture-sensitive
1175 public int IndexOf (String value)
1178 throw new ArgumentNullException ("value");
1179 if (value.length == 0)
1181 if (this.length == 0)
1183 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length);
1186 public int IndexOf (String value, int startIndex)
1188 return IndexOf (value, startIndex, this.length - startIndex);
1191 public int IndexOf (String value, int startIndex, int count)
1195 throw new ArgumentNullException ("value");
1197 throw new ArgumentNullException ("string2");
1199 if (startIndex < 0 || startIndex > this.length)
1200 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1201 if (count < 0 || startIndex > this.length - count)
1202 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1204 if (value.length == 0)
1207 if (startIndex == 0 && this.length == 0)
1213 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1216 // Following methods are culture-insensitive
1217 public int LastIndexOfAny (char [] anyOf)
1220 throw new ArgumentNullException ();
1222 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1225 public int LastIndexOfAny (char [] anyOf, int startIndex)
1228 throw new ArgumentNullException ();
1230 if (startIndex < 0 || startIndex >= this.length)
1231 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1233 if (this.length == 0)
1236 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1239 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1242 throw new ArgumentNullException ();
1244 if ((startIndex < 0) || (startIndex >= this.Length))
1245 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1246 if ((count < 0) || (count > this.Length))
1247 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1248 if (startIndex - count + 1 < 0)
1249 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1251 if (this.length == 0)
1254 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1257 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1259 if (anyOf.Length == 1)
1260 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1262 fixed (char* start = this, testStart = anyOf) {
1263 char* ptr = start + startIndex;
1264 char* ptrEnd = ptr - count;
1266 char* testEnd = testStart + anyOf.Length;
1268 while (ptr != ptrEnd) {
1270 while (test != testEnd) {
1272 return (int)(ptr - start);
1281 // Following methods are culture-insensitive
1282 public int LastIndexOf (char value)
1284 if (this.length == 0)
1287 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1290 public int LastIndexOf (char value, int startIndex)
1292 return LastIndexOf (value, startIndex, startIndex + 1);
1295 public int LastIndexOf (char value, int startIndex, int count)
1297 if (startIndex == 0 && this.length == 0)
1300 // >= for char (> for string)
1301 if ((startIndex < 0) || (startIndex >= this.Length))
1302 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1303 if ((count < 0) || (count > this.Length))
1304 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1305 if (startIndex - count + 1 < 0)
1306 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1308 return LastIndexOfUnchecked (value, startIndex, count);
1311 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1313 // It helps JIT compiler to optimize comparison
1314 int value_32 = (int)value;
1316 fixed (char* start = &start_char) {
1317 char* ptr = start + startIndex;
1318 char* end_ptr = ptr - (count >> 3 << 3);
1320 while (ptr != end_ptr) {
1321 if (*ptr == value_32)
1322 return (int)(ptr - start);
1323 if (ptr[-1] == value_32)
1324 return (int)(ptr - start) - 1;
1325 if (ptr[-2] == value_32)
1326 return (int)(ptr - start) - 2;
1327 if (ptr[-3] == value_32)
1328 return (int)(ptr - start) - 3;
1329 if (ptr[-4] == value_32)
1330 return (int)(ptr - start) - 4;
1331 if (ptr[-5] == value_32)
1332 return (int)(ptr - start) - 5;
1333 if (ptr[-6] == value_32)
1334 return (int)(ptr - start) - 6;
1335 if (ptr[-7] == value_32)
1336 return (int)(ptr - start) - 7;
1341 end_ptr -= count & 0x07;
1342 while (ptr != end_ptr) {
1343 if (*ptr == value_32)
1344 return (int)(ptr - start);
1352 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1356 int end = startIndex - count;
1357 char c = Char.ToUpperInvariant (value);
1358 fixed (char* s = &start_char) {
1359 for (int i = startIndex; i > end; i--)
1360 if (Char.ToUpperInvariant (s [i]) == c)
1366 // Following methods are culture-sensitive
1367 public int LastIndexOf (String value)
1369 if (this.length == 0)
1370 // This overload does additional checking
1371 return LastIndexOf (value, 0, 0);
1373 return LastIndexOf (value, this.length - 1, this.length);
1376 public int LastIndexOf (String value, int startIndex)
1378 int max = startIndex;
1379 if (max < this.Length)
1381 return LastIndexOf (value, startIndex, max);
1384 public int LastIndexOf (String value, int startIndex, int count)
1388 throw new ArgumentNullException ("value");
1390 throw new ArgumentNullException ("string2");
1393 // -1 > startIndex > for string (0 > startIndex >= for char)
1394 if ((startIndex < -1) || (startIndex > this.Length))
1395 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1396 if ((count < 0) || (count > this.Length))
1397 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1398 if (startIndex - count + 1 < 0)
1399 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1401 if (value.Length == 0)
1404 if (startIndex == 0 && this.length == 0)
1407 // This check is needed to match undocumented MS behaviour
1408 if (this.length == 0 && value.length > 0)
1414 if (startIndex == this.Length)
1416 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1420 public bool Contains (String value)
1422 return IndexOf (value) != -1;
1425 public static bool IsNullOrEmpty (String value)
1427 return (value == null) || (value.Length == 0);
1430 public string Normalize ()
1432 return Normalization.Normalize (this, 0);
1435 public string Normalize (NormalizationForm normalizationForm)
1437 switch (normalizationForm) {
1439 return Normalization.Normalize (this, 0);
1440 case NormalizationForm.FormD:
1441 return Normalization.Normalize (this, 1);
1442 case NormalizationForm.FormKC:
1443 return Normalization.Normalize (this, 2);
1444 case NormalizationForm.FormKD:
1445 return Normalization.Normalize (this, 3);
1449 public bool IsNormalized ()
1451 return Normalization.IsNormalized (this, 0);
1454 public bool IsNormalized (NormalizationForm normalizationForm)
1456 switch (normalizationForm) {
1458 return Normalization.IsNormalized (this, 0);
1459 case NormalizationForm.FormD:
1460 return Normalization.IsNormalized (this, 1);
1461 case NormalizationForm.FormKC:
1462 return Normalization.IsNormalized (this, 2);
1463 case NormalizationForm.FormKD:
1464 return Normalization.IsNormalized (this, 3);
1468 public string Remove (int startIndex)
1471 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1472 if (startIndex >= this.length)
1473 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1475 return Remove (startIndex, this.length - startIndex);
1479 public String PadLeft (int totalWidth)
1481 return PadLeft (totalWidth, ' ');
1484 public unsafe String PadLeft (int totalWidth, char paddingChar)
1486 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1489 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1491 if (totalWidth < this.length)
1494 String tmp = InternalAllocateStr (totalWidth);
1496 fixed (char* dest = tmp, src = this) {
1497 char* padPos = dest;
1498 char* padTo = dest + (totalWidth - length);
1499 while (padPos != padTo)
1500 *padPos++ = paddingChar;
1502 CharCopy (padTo, src, length);
1507 public String PadRight (int totalWidth)
1509 return PadRight (totalWidth, ' ');
1512 public unsafe String PadRight (int totalWidth, char paddingChar)
1514 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1517 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1519 if (totalWidth < this.length)
1522 String tmp = InternalAllocateStr (totalWidth);
1524 fixed (char* dest = tmp, src = this) {
1525 CharCopy (dest, src, length);
1527 char* padPos = dest + length;
1528 char* padTo = dest + totalWidth;
1529 while (padPos != padTo)
1530 *padPos++ = paddingChar;
1535 public bool StartsWith (String value)
1538 throw new ArgumentNullException ("value");
1540 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1544 [ComVisible (false)]
1545 public bool StartsWith (string value, StringComparison comparisonType)
1548 throw new ArgumentNullException ("value");
1550 switch (comparisonType) {
1551 case StringComparison.CurrentCulture:
1552 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1553 case StringComparison.CurrentCultureIgnoreCase:
1554 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1555 case StringComparison.InvariantCulture:
1556 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1557 case StringComparison.InvariantCultureIgnoreCase:
1558 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1559 case StringComparison.Ordinal:
1560 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1561 case StringComparison.OrdinalIgnoreCase:
1562 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1564 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1565 throw new ArgumentException (msg, "comparisonType");
1569 [ComVisible (false)]
1570 public bool EndsWith (string value, StringComparison comparisonType)
1573 throw new ArgumentNullException ("value");
1575 switch (comparisonType) {
1576 case StringComparison.CurrentCulture:
1577 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1578 case StringComparison.CurrentCultureIgnoreCase:
1579 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1580 case StringComparison.InvariantCulture:
1581 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1582 case StringComparison.InvariantCultureIgnoreCase:
1583 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1584 case StringComparison.Ordinal:
1585 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1586 case StringComparison.OrdinalIgnoreCase:
1587 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1589 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1590 throw new ArgumentException (msg, "comparisonType");
1600 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1602 if (culture == null)
1603 culture = CultureInfo.CurrentCulture;
1605 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1608 // Following method is culture-insensitive
1609 public unsafe String Replace (char oldChar, char newChar)
1611 if (this.length == 0 || oldChar == newChar)
1614 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1615 if (start_pos == -1)
1621 string tmp = InternalAllocateStr (length);
1622 fixed (char* dest = tmp, src = &start_char) {
1624 CharCopy (dest, src, start_pos);
1626 char* end_ptr = dest + length;
1627 char* dest_ptr = dest + start_pos;
1628 char* src_ptr = src + start_pos;
1630 while (dest_ptr != end_ptr) {
1631 if (*src_ptr == oldChar)
1632 *dest_ptr = newChar;
1634 *dest_ptr = *src_ptr;
1643 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1644 public String Replace (String oldValue, String newValue)
1646 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1647 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1649 if (oldValue == null)
1650 throw new ArgumentNullException ("oldValue");
1652 if (oldValue.Length == 0)
1653 throw new ArgumentException ("oldValue is the empty string.");
1655 if (this.Length == 0)
1658 if (newValue == null)
1659 newValue = String.Empty;
1661 return ReplaceUnchecked (oldValue, newValue);
1664 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1666 if (oldValue.length > length)
1668 if (oldValue.length == 1 && newValue.length == 1) {
1669 return Replace (oldValue[0], newValue[0]);
1670 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1671 // because the length of the result would be this.length and length calculation unneccesary
1674 const int maxValue = 200; // Allocate 800 byte maximum
1675 int* dat = stackalloc int[maxValue];
1676 fixed (char* source = this, replace = newValue) {
1677 int i = 0, count = 0;
1678 while (i < length) {
1679 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1683 if (count < maxValue)
1684 dat[count++] = found;
1686 return ReplaceFallback (oldValue, newValue, maxValue);
1688 i = found + oldValue.length;
1692 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1693 String tmp = InternalAllocateStr (nlen);
1695 int curPos = 0, lastReadPos = 0;
1696 fixed (char* dest = tmp) {
1697 for (int j = 0; j < count; j++) {
1698 int precopy = dat[j] - lastReadPos;
1699 CharCopy (dest + curPos, source + lastReadPos, precopy);
1701 lastReadPos = dat[j] + oldValue.length;
1702 CharCopy (dest + curPos, replace, newValue.length);
1703 curPos += newValue.length;
1705 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1711 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1713 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1714 StringBuilder sb = new StringBuilder (lengthEstimate);
1715 for (int i = 0; i < length;) {
1716 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1718 sb.Append (SubstringUnchecked (i, length - i));
1721 sb.Append (SubstringUnchecked (i, found - i));
1722 sb.Append (newValue);
1723 i = found + oldValue.Length;
1725 return sb.ToString ();
1729 public unsafe String Remove (int startIndex, int count)
1732 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1734 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1735 if (startIndex > this.length - count)
1736 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1738 String tmp = InternalAllocateStr (this.length - count);
1740 fixed (char *dest = tmp, src = this) {
1742 CharCopy (dst, src, startIndex);
1743 int skip = startIndex + count;
1745 CharCopy (dst, src + skip, length - skip);
1750 public String ToLower ()
1752 return ToLower (CultureInfo.CurrentCulture);
1755 public String ToLower (CultureInfo culture)
1757 if (culture == null)
1758 throw new ArgumentNullException ("culture");
1760 if (culture.LCID == 0x007F) // Invariant
1761 return ToLowerInvariant ();
1763 return culture.TextInfo.ToLower (this);
1767 public unsafe String ToLowerInvariant ()
1769 internal unsafe String ToLowerInvariant ()
1772 string tmp = InternalAllocateStr (length);
1773 fixed (char* source = &start_char, dest = tmp) {
1775 char* destPtr = (char*)dest;
1776 char* sourcePtr = (char*)source;
1778 for (int n = 0; n < length; n++) {
1779 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1787 public String ToUpper ()
1789 return ToUpper (CultureInfo.CurrentCulture);
1792 public String ToUpper (CultureInfo culture)
1794 if (culture == null)
1795 throw new ArgumentNullException ("culture");
1797 if (culture.LCID == 0x007F) // Invariant
1798 return ToUpperInvariant ();
1800 return culture.TextInfo.ToUpper (this);
1804 public unsafe String ToUpperInvariant ()
1806 internal unsafe String ToUpperInvariant ()
1809 string tmp = InternalAllocateStr (length);
1810 fixed (char* source = &start_char, dest = tmp) {
1812 char* destPtr = (char*)dest;
1813 char* sourcePtr = (char*)source;
1815 for (int n = 0; n < length; n++) {
1816 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1824 public override String ToString ()
1829 public String ToString (IFormatProvider provider)
1834 public static String Format (String format, Object arg0)
1836 return Format (null, format, new Object[] {arg0});
1839 public static String Format (String format, Object arg0, Object arg1)
1841 return Format (null, format, new Object[] {arg0, arg1});
1844 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1846 return Format (null, format, new Object[] {arg0, arg1, arg2});
1849 public static string Format (string format, params object[] args)
1851 return Format (null, format, args);
1854 public static string Format (IFormatProvider provider, string format, params object[] args)
1856 StringBuilder b = new StringBuilder ();
1857 FormatHelper (b, provider, format, args);
1858 return b.ToString ();
1861 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1864 throw new ArgumentNullException ("format");
1866 throw new ArgumentNullException ("args");
1870 while (ptr < format.length) {
1871 char c = format[ptr ++];
1874 result.Append (format, start, ptr - start - 1);
1876 // check for escaped open bracket
1878 if (format[ptr] == '{') {
1889 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1890 if (n >= args.Length)
1891 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1895 object arg = args[n];
1898 ICustomFormatter formatter = null;
1899 if (provider != null)
1900 formatter = provider.GetFormat (typeof (ICustomFormatter))
1901 as ICustomFormatter;
1904 else if (formatter != null)
1905 str = formatter.Format (arg_format, arg, provider);
1906 else if (arg is IFormattable)
1907 str = ((IFormattable)arg).ToString (arg_format, provider);
1909 str = arg.ToString ();
1911 // pad formatted string and append to result
1913 if (width > str.length) {
1914 const char padchar = ' ';
1915 int padlen = width - str.length;
1918 result.Append (str);
1919 result.Append (padchar, padlen);
1922 result.Append (padchar, padlen);
1923 result.Append (str);
1927 result.Append (str);
1931 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1932 result.Append (format, start, ptr - start - 1);
1935 else if (c == '}') {
1936 throw new FormatException ("Input string was not in a correct format.");
1940 if (start < format.length)
1941 result.Append (format, start, format.Length - start);
1944 public unsafe static String Copy (String str)
1947 throw new ArgumentNullException ("str");
1949 int length = str.length;
1951 String tmp = InternalAllocateStr (length);
1953 fixed (char *dest = tmp, src = str) {
1954 CharCopy (dest, src, length);
1960 public static String Concat (Object arg0)
1963 return String.Empty;
1965 return arg0.ToString ();
1968 public unsafe static String Concat (Object arg0, Object arg1)
1972 s1 = (arg0 != null) ? arg0.ToString () : null;
1973 s2 = (arg1 != null) ? arg1.ToString () : null;
1977 return String.Empty;
1980 } else if (s2 == null)
1983 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1984 if (s1.Length != 0) {
1985 fixed (char *dest = tmp, src = s1) {
1986 CharCopy (dest, src, s1.length);
1989 if (s2.Length != 0) {
1990 fixed (char *dest = tmp, src = s2) {
1991 CharCopy (dest + s1.Length, src, s2.length);
1998 public static String Concat (Object arg0, Object arg1, Object arg2)
2004 s1 = arg0.ToString ();
2009 s2 = arg1.ToString ();
2014 s3 = arg2.ToString ();
2016 return Concat (s1, s2, s3);
2019 #if ! BOOTSTRAP_WITH_OLDLIB
2020 [CLSCompliant(false)]
2021 public static String Concat (Object arg0, Object arg1, Object arg2,
2022 Object arg3, __arglist)
2024 string s1, s2, s3, s4;
2029 s1 = arg0.ToString ();
2034 s2 = arg1.ToString ();
2039 s3 = arg2.ToString ();
2041 ArgIterator iter = new ArgIterator (__arglist);
2042 int argCount = iter.GetRemainingCount();
2044 StringBuilder sb = new StringBuilder ();
2046 sb.Append (arg3.ToString ());
2048 for (int i = 0; i < argCount; i++) {
2049 TypedReference typedRef = iter.GetNextArg ();
2050 sb.Append (TypedReference.ToObject (typedRef));
2053 s4 = sb.ToString ();
2055 return Concat (s1, s2, s3, s4);
2059 public unsafe static String Concat (String str0, String str1)
2061 if (str0 == null || str0.Length == 0) {
2062 if (str1 == null || str1.Length == 0)
2063 return String.Empty;
2067 if (str1 == null || str1.Length == 0)
2070 String tmp = InternalAllocateStr (str0.length + str1.length);
2072 fixed (char *dest = tmp, src = str0)
2073 CharCopy (dest, src, str0.length);
2074 fixed (char *dest = tmp, src = str1)
2075 CharCopy (dest + str0.Length, src, str1.length);
2080 public unsafe static String Concat (String str0, String str1, String str2)
2082 if (str0 == null || str0.Length == 0){
2083 if (str1 == null || str1.Length == 0){
2084 if (str2 == null || str2.Length == 0)
2085 return String.Empty;
2088 if (str2 == null || str2.Length == 0)
2091 str0 = String.Empty;
2093 if (str1 == null || str1.Length == 0){
2094 if (str2 == null || str2.Length == 0)
2097 str1 = String.Empty;
2099 if (str2 == null || str2.Length == 0)
2100 str2 = String.Empty;
2104 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2106 if (str0.Length != 0) {
2107 fixed (char *dest = tmp, src = str0) {
2108 CharCopy (dest, src, str0.length);
2111 if (str1.Length != 0) {
2112 fixed (char *dest = tmp, src = str1) {
2113 CharCopy (dest + str0.Length, src, str1.length);
2116 if (str2.Length != 0) {
2117 fixed (char *dest = tmp, src = str2) {
2118 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2125 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2127 if (str0 == null && str1 == null && str2 == null && str3 == null)
2128 return String.Empty;
2131 str0 = String.Empty;
2133 str1 = String.Empty;
2135 str2 = String.Empty;
2137 str3 = String.Empty;
2139 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2141 if (str0.Length != 0) {
2142 fixed (char *dest = tmp, src = str0) {
2143 CharCopy (dest, src, str0.length);
2146 if (str1.Length != 0) {
2147 fixed (char *dest = tmp, src = str1) {
2148 CharCopy (dest + str0.Length, src, str1.length);
2151 if (str2.Length != 0) {
2152 fixed (char *dest = tmp, src = str2) {
2153 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2156 if (str3.Length != 0) {
2157 fixed (char *dest = tmp, src = str3) {
2158 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2165 public static String Concat (params Object[] args)
2168 throw new ArgumentNullException ("args");
2170 int argLen = args.Length;
2172 return String.Empty;
2174 string [] strings = new string [argLen];
2176 for (int i = 0; i < argLen; i++) {
2177 if (args[i] != null) {
2178 strings[i] = args[i].ToString ();
2179 len += strings[i].length;
2183 return String.Empty;
2185 return ConcatInternal (strings, len);
2188 public static String Concat (params String[] values)
2191 throw new ArgumentNullException ("values");
2194 for (int i = 0; i < values.Length; i++) {
2195 String s = values[i];
2200 return String.Empty;
2202 return ConcatInternal (values, len);
2205 private static unsafe String ConcatInternal (String[] values, int length)
2207 String tmp = InternalAllocateStr (length);
2209 fixed (char* dest = tmp) {
2211 for (int i = 0; i < values.Length; i++) {
2212 String source = values[i];
2213 if (source != null) {
2214 fixed (char* src = source) {
2215 CharCopy (dest + pos, src, source.length);
2217 pos += source.Length;
2224 public unsafe String Insert (int startIndex, String value)
2227 throw new ArgumentNullException ("value");
2229 if (startIndex < 0 || startIndex > this.length)
2230 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2232 if (value.Length == 0)
2234 if (this.Length == 0)
2236 String tmp = InternalAllocateStr (this.length + value.length);
2238 fixed (char *dest = tmp, src = this, val = value) {
2240 CharCopy (dst, src, startIndex);
2242 CharCopy (dst, val, value.length);
2243 dst += value.length;
2244 CharCopy (dst, src + startIndex, length - startIndex);
2249 public static string Intern (string str)
2252 throw new ArgumentNullException ("str");
2254 return InternalIntern (str);
2257 public static string IsInterned (string str)
2260 throw new ArgumentNullException ("str");
2262 return InternalIsInterned (str);
2265 public static string Join (string separator, string [] value)
2268 throw new ArgumentNullException ("value");
2269 if (separator == null)
2270 separator = String.Empty;
2272 return JoinUnchecked (separator, value, 0, value.Length);
2275 public static string Join (string separator, string[] value, int startIndex, int count)
2278 throw new ArgumentNullException ("value");
2280 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2282 throw new ArgumentOutOfRangeException ("count", "< 0");
2283 if (startIndex > value.Length - count)
2284 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2286 if (startIndex == value.Length)
2287 return String.Empty;
2288 if (separator == null)
2289 separator = String.Empty;
2291 return JoinUnchecked (separator, value, startIndex, count);
2294 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2296 // Unchecked parameters
2297 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2298 // separator and value must not be null
2301 int maxIndex = startIndex + count;
2302 // Precount the number of characters that the resulting string will have
2303 for (int i = startIndex; i < maxIndex; i++) {
2304 String s = value[i];
2308 length += separator.length * (count - 1);
2310 return String.Empty;
2312 String tmp = InternalAllocateStr (length);
2315 fixed (char* dest = tmp, sepsrc = separator) {
2316 // Copy each string from value except the last one and add a separator for each
2318 for (int i = startIndex; i < maxIndex; i++) {
2319 String source = value[i];
2320 if (source != null) {
2321 if (source.Length > 0) {
2322 fixed (char* src = source)
2323 CharCopy (dest + pos, src, source.Length);
2324 pos += source.Length;
2327 if (separator.Length > 0) {
2328 CharCopy (dest + pos, sepsrc, separator.Length);
2329 pos += separator.Length;
2332 // Append last string that does not get an additional separator
2333 String sourceLast = value[maxIndex];
2334 if (sourceLast != null) {
2335 if (sourceLast.Length > 0) {
2336 fixed (char* src = sourceLast)
2337 CharCopy (dest + pos, src, sourceLast.Length);
2344 bool IConvertible.ToBoolean (IFormatProvider provider)
2346 return Convert.ToBoolean (this, provider);
2349 byte IConvertible.ToByte (IFormatProvider provider)
2351 return Convert.ToByte (this, provider);
2354 char IConvertible.ToChar (IFormatProvider provider)
2356 return Convert.ToChar (this, provider);
2359 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2361 return Convert.ToDateTime (this, provider);
2364 decimal IConvertible.ToDecimal (IFormatProvider provider)
2366 return Convert.ToDecimal (this, provider);
2369 double IConvertible.ToDouble (IFormatProvider provider)
2371 return Convert.ToDouble (this, provider);
2374 short IConvertible.ToInt16 (IFormatProvider provider)
2376 return Convert.ToInt16 (this, provider);
2379 int IConvertible.ToInt32 (IFormatProvider provider)
2381 return Convert.ToInt32 (this, provider);
2384 long IConvertible.ToInt64 (IFormatProvider provider)
2386 return Convert.ToInt64 (this, provider);
2390 #pragma warning disable 3019
2391 [CLSCompliant (false)]
2393 sbyte IConvertible.ToSByte (IFormatProvider provider)
2395 return Convert.ToSByte (this, provider);
2398 #pragma warning restore 3019
2401 float IConvertible.ToSingle (IFormatProvider provider)
2403 return Convert.ToSingle (this, provider);
2406 object IConvertible.ToType (Type type, IFormatProvider provider)
2408 return Convert.ToType (this, type, provider);
2412 #pragma warning disable 3019
2413 [CLSCompliant (false)]
2415 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2417 return Convert.ToUInt16 (this, provider);
2420 #pragma warning restore 3019
2424 #pragma warning disable 3019
2425 [CLSCompliant (false)]
2427 uint IConvertible.ToUInt32 (IFormatProvider provider)
2429 return Convert.ToUInt32 (this, provider);
2432 #pragma warning restore 3019
2436 #pragma warning disable 3019
2437 [CLSCompliant (false)]
2439 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2441 return Convert.ToUInt64 (this, provider);
2444 #pragma warning restore 3019
2453 public CharEnumerator GetEnumerator ()
2455 return new CharEnumerator (this);
2459 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2461 return new CharEnumerator (this);
2465 IEnumerator IEnumerable.GetEnumerator ()
2467 return new CharEnumerator (this);
2470 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2471 out bool left_align, out string format)
2473 // parses format specifier of form:
2479 // N = argument number (non-negative integer)
2481 n = ParseDecimal (str, ref ptr);
2483 throw new FormatException ("Input string was not in a correct format.");
2485 // M = width (non-negative integer)
2487 if (str[ptr] == ',') {
2488 // White space between ',' and number or sign.
2490 while (Char.IsWhiteSpace (str [ptr]))
2494 format = str.Substring (start, ptr - start);
2496 left_align = (str [ptr] == '-');
2500 width = ParseDecimal (str, ref ptr);
2502 throw new FormatException ("Input string was not in a correct format.");
2507 format = String.Empty;
2510 // F = argument format (string)
2512 if (str[ptr] == ':') {
2514 while (str[ptr] != '}')
2517 format += str.Substring (start, ptr - start);
2522 if (str[ptr ++] != '}')
2523 throw new FormatException ("Input string was not in a correct format.");
2525 catch (IndexOutOfRangeException) {
2526 throw new FormatException ("Input string was not in a correct format.");
2530 private static int ParseDecimal (string str, ref int ptr)
2536 if (c < '0' || '9' < c)
2539 n = n * 10 + c - '0';
2550 internal unsafe void InternalSetChar (int idx, char val)
2552 if ((uint) idx >= (uint) Length)
2553 throw new ArgumentOutOfRangeException ("idx");
2555 fixed (char * pStr = &start_char)
2561 internal unsafe void InternalSetLength (int newLength)
2563 if (newLength > length)
2564 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2566 // zero terminate, we can pass string objects directly via pinvoke
2567 // we also zero the rest of the string, since the new GC needs to be
2568 // able to handle the changing size (it will skip the 0 bytes).
2569 fixed (char * pStr = &start_char) {
2570 char *p = pStr + newLength;
2571 char *end = pStr + length;
2581 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2583 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2584 public unsafe override int GetHashCode ()
2586 fixed (char * c = this) {
2588 char * end = cc + length - 1;
2590 for (;cc < end; cc += 2) {
2591 h = (h << 5) - h + *cc;
2592 h = (h << 5) - h + cc [1];
2596 h = (h << 5) - h + *cc;
2601 internal unsafe int GetCaseInsensitiveHashCode ()
2603 fixed (char * c = this) {
2605 char * end = cc + length - 1;
2607 for (;cc < end; cc += 2) {
2608 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2609 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2613 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2618 // Certain constructors are redirected to CreateString methods with
2619 // matching argument list. The this pointer should not be used.
2620 #pragma warning disable 169
2621 private unsafe String CreateString (sbyte* value)
2624 return String.Empty;
2626 byte* bytes = (byte*) value;
2630 while (bytes++ [0] != 0)
2632 } catch (NullReferenceException) {
2633 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2635 } catch (AccessViolationException) {
2636 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2640 return CreateString (value, 0, length, null);
2643 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2645 return CreateString (value, startIndex, length, null);
2648 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2651 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2653 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2654 if (value + startIndex < value)
2655 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2657 bool isDefaultEncoding;
2659 if (isDefaultEncoding = (enc == null)) {
2662 throw new ArgumentNullException ("value");
2665 if (value == null || length == 0)
2667 return String.Empty;
2669 enc = Encoding.Default;
2672 byte [] bytes = new byte [length];
2675 fixed (byte* bytePtr = bytes)
2677 memcpy (bytePtr, (byte*) (value + startIndex), length);
2678 } catch (NullReferenceException) {
2680 if (!isDefaultEncoding)
2684 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2686 } catch (AccessViolationException) {
2687 if (!isDefaultEncoding)
2690 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2694 // GetString () is called even when length == 0
2695 return enc.GetString (bytes);
2698 unsafe string CreateString (char *value)
2701 return string.Empty;
2708 string result = InternalAllocateStr (i);
2711 fixed (char *dest = result) {
2712 CharCopy (dest, value, i);
2718 unsafe string CreateString (char *value, int startIndex, int length)
2721 return string.Empty;
2723 throw new ArgumentNullException ("value");
2725 throw new ArgumentOutOfRangeException ("startIndex");
2727 throw new ArgumentOutOfRangeException ("length");
2729 string result = InternalAllocateStr (length);
2731 fixed (char *dest = result) {
2732 CharCopy (dest, value + startIndex, length);
2737 unsafe string CreateString (char [] val, int startIndex, int length)
2740 throw new ArgumentNullException ("value");
2742 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2744 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2745 if (startIndex > val.Length - length)
2746 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2748 return string.Empty;
2750 string result = InternalAllocateStr (length);
2752 fixed (char *dest = result, src = val) {
2753 CharCopy (dest, src + startIndex, length);
2758 unsafe string CreateString (char [] val)
2761 return string.Empty;
2762 if (val.Length == 0)
2763 return string.Empty;
2764 string result = InternalAllocateStr (val.Length);
2766 fixed (char *dest = result, src = val) {
2767 CharCopy (dest, src, val.Length);
2772 unsafe string CreateString (char c, int count)
2775 throw new ArgumentOutOfRangeException ("count");
2777 return string.Empty;
2778 string result = InternalAllocateStr (count);
2779 fixed (char *dest = result) {
2781 char *end = p + count;
2789 #pragma warning restore 169
2791 /* helpers used by the runtime as well as above or eslewhere in corlib */
2792 internal static unsafe void memset (byte *dest, int val, int len)
2803 val = val | (val << 8);
2804 val = val | (val << 16);
2807 int rest = (int)dest & 3;
2815 } while (rest != 0);
2818 ((int*)dest) [0] = val;
2819 ((int*)dest) [1] = val;
2820 ((int*)dest) [2] = val;
2821 ((int*)dest) [3] = val;
2826 ((int*)dest) [0] = val;
2838 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2839 /*while (size >= 32) {
2840 // using long is better than int and slower than double
2841 // FIXME: enable this only on correct alignment or on platforms
2842 // that can tolerate unaligned reads/writes of doubles
2843 ((double*)dest) [0] = ((double*)src) [0];
2844 ((double*)dest) [1] = ((double*)src) [1];
2845 ((double*)dest) [2] = ((double*)src) [2];
2846 ((double*)dest) [3] = ((double*)src) [3];
2851 while (size >= 16) {
2852 ((int*)dest) [0] = ((int*)src) [0];
2853 ((int*)dest) [1] = ((int*)src) [1];
2854 ((int*)dest) [2] = ((int*)src) [2];
2855 ((int*)dest) [3] = ((int*)src) [3];
2861 ((int*)dest) [0] = ((int*)src) [0];
2867 ((byte*)dest) [0] = ((byte*)src) [0];
2873 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2875 ((short*)dest) [0] = ((short*)src) [0];
2876 ((short*)dest) [1] = ((short*)src) [1];
2877 ((short*)dest) [2] = ((short*)src) [2];
2878 ((short*)dest) [3] = ((short*)src) [3];
2884 ((short*)dest) [0] = ((short*)src) [0];
2890 ((byte*)dest) [0] = ((byte*)src) [0];
2892 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2894 ((byte*)dest) [0] = ((byte*)src) [0];
2895 ((byte*)dest) [1] = ((byte*)src) [1];
2896 ((byte*)dest) [2] = ((byte*)src) [2];
2897 ((byte*)dest) [3] = ((byte*)src) [3];
2898 ((byte*)dest) [4] = ((byte*)src) [4];
2899 ((byte*)dest) [5] = ((byte*)src) [5];
2900 ((byte*)dest) [6] = ((byte*)src) [6];
2901 ((byte*)dest) [7] = ((byte*)src) [7];
2907 ((byte*)dest) [0] = ((byte*)src) [0];
2908 ((byte*)dest) [1] = ((byte*)src) [1];
2914 ((byte*)dest) [0] = ((byte*)src) [0];
2917 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2918 // FIXME: if pointers are not aligned, try to align them
2919 // so a faster routine can be used. Handle the case where
2920 // the pointers can't be reduced to have the same alignment
2921 // (just ignore the issue on x86?)
2922 if ((((int)dest | (int)src) & 3) != 0) {
2923 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2929 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2930 ((short*)dest) [0] = ((short*)src) [0];
2935 if ((((int)dest | (int)src) & 1) != 0) {
2936 memcpy1 (dest, src, size);
2939 if ((((int)dest | (int)src) & 2) != 0) {
2940 memcpy2 (dest, src, size);
2944 memcpy4 (dest, src, size);
2947 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2948 // Same rules as for memcpy, but with the premise that
2949 // chars can only be aligned to even addresses if their
2950 // enclosing types are correctly aligned
2951 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2952 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2953 ((short*)dest) [0] = ((short*)src) [0];
2958 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2959 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2963 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2966 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2970 for (int i = count; i > 0; i--) {
2977 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2979 fixed (char* dest = target, src = source)
2980 CharCopy (dest + targetIndex, src + sourceIndex, count);
2983 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2985 fixed (char* dest = target, src = source)
2986 CharCopy (dest + targetIndex, src + sourceIndex, count);
2989 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2990 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2992 fixed (char* dest = target, src = source)
2993 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
2996 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2997 unsafe public extern String (char *value);
2999 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3000 unsafe public extern String (char *value, int startIndex, int length);
3002 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3003 unsafe public extern String (sbyte *value);
3005 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3006 unsafe public extern String (sbyte *value, int startIndex, int length);
3008 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3009 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3011 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3012 public extern String (char [] value, int startIndex, int length);
3014 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3015 public extern String (char [] value);
3017 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3018 public extern String (char c, int count);
3020 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3021 // private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
3023 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3024 // private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
3026 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3027 // private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
3029 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3030 private extern String[] InternalSplit (char[] separator, int count, int options);
3032 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3033 // private extern String InternalTrim (char[] chars, int typ);
3035 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3036 // private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
3038 // [MethodImplAttribute (MethodImplOptions.InternalCall)]
3039 // private extern String InternalPad (int width, char chr, bool right);
3041 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3042 internal extern static String InternalAllocateStr (int length);
3044 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3045 internal extern static void InternalStrcpy (String dest, int destPos, String src);
3047 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3048 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
3050 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3051 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
3053 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3054 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
3056 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3057 private extern static string InternalIntern (string str);
3059 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3060 private extern static string InternalIsInterned (string str);