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;
48 using System.Collections.Generic;
49 using System.Runtime.ConstrainedExecution;
50 using System.Runtime.InteropServices;
51 using Mono.Globalization.Unicode;
58 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
60 [NonSerialized] private int length;
61 [NonSerialized] private char start_char;
63 public static readonly String Empty = "";
65 internal static readonly int LOS_limit = GetLOSLimit ();
67 public static unsafe bool Equals (string a, string b)
69 if ((a as object) == (b as object))
72 if (a == null || b == null)
80 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
85 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
86 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
87 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
88 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
97 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
98 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
107 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
115 return len == 0 || *s1_ptr == *s2_ptr;
119 public static bool operator == (String a, String b)
121 return Equals (a, b);
124 public static bool operator != (String a, String b)
126 return !Equals (a, b);
129 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
130 public override bool Equals (Object obj)
132 return Equals (this, obj as String);
135 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
136 public bool Equals (String value)
138 return Equals (this, value);
141 [IndexerName ("Chars")]
142 public unsafe char this [int index] {
144 if (index < 0 || index >= length)
145 throw new IndexOutOfRangeException ();
146 fixed (char* c = &start_char)
151 public Object Clone ()
156 public TypeCode GetTypeCode ()
158 return TypeCode.String;
161 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
163 if (destination == null)
164 throw new ArgumentNullException ("destination");
166 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
167 if (destinationIndex < 0)
168 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
170 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
171 if (sourceIndex > Length - count)
172 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
173 if (destinationIndex > destination.Length - count)
174 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
176 fixed (char* dest = destination, src = this)
177 CharCopy (dest + destinationIndex, src + sourceIndex, count);
180 public char[] ToCharArray ()
182 return ToCharArray (0, length);
185 public unsafe char[] ToCharArray (int startIndex, int length)
188 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
190 throw new ArgumentOutOfRangeException ("length", "< 0");
191 if (startIndex > this.length - length)
192 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
194 char[] tmp = new char [length];
195 fixed (char* dest = tmp, src = this)
196 CharCopy (dest, src + startIndex, length);
200 public String [] Split (params char [] separator)
202 return Split (separator, Int32.MaxValue);
205 public String[] Split (char[] separator, int count)
207 if (separator == null || separator.Length == 0)
208 separator = WhiteChars;
211 throw new ArgumentOutOfRangeException ("count");
214 return new String[0];
217 return new String[1] { this };
219 return InternalSplit (separator, count, 0);
223 [MonoDocumentationNote ("code should be moved to managed")]
224 public String[] Split (char[] separator, int count, StringSplitOptions options)
226 if (separator == null || separator.Length == 0)
227 return Split (WhiteChars, count, options);
230 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
231 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
232 throw new ArgumentException ("Illegal enum value: " + options + ".");
235 return new string [0];
237 return InternalSplit (separator, count, (int)options);
241 public String[] Split (string[] separator, int count, StringSplitOptions options)
243 if (separator == null || separator.Length == 0)
244 return Split (WhiteChars, count, options);
247 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
248 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
249 throw new ArgumentException ("Illegal enum value: " + options + ".");
251 return new String [] { this };
253 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
255 if (count == 0 || (this == String.Empty && removeEmpty))
256 return new String [0];
258 List<String> arr = new List<String> ();
262 while (pos < this.Length) {
264 int matchPos = Int32.MaxValue;
266 // Find the first position where any of the separators matches
267 for (int i = 0; i < separator.Length; ++i) {
268 string sep = separator [i];
269 if (sep == null || sep == String.Empty)
272 int match = IndexOf (sep, pos);
273 if (match > -1 && match < matchPos) {
279 if (matchIndex == -1)
282 if (!(matchPos == pos && removeEmpty)) {
283 if (arr.Count == count - 1)
285 arr.Add (this.Substring (pos, matchPos - pos));
288 pos = matchPos + separator [matchIndex].Length;
294 return new String [] { this };
296 // string contained only separators
297 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
298 return new String [0];
300 if (!(removeEmpty && pos == this.Length))
301 arr.Add (this.Substring (pos));
303 return arr.ToArray ();
307 public String[] Split (char[] separator, StringSplitOptions options)
309 return Split (separator, Int32.MaxValue, options);
313 public String[] Split (String[] separator, StringSplitOptions options)
315 return Split (separator, Int32.MaxValue, options);
318 public String Substring (int startIndex)
322 if (startIndex < 0 || startIndex > this.length)
323 throw new ArgumentOutOfRangeException ("startIndex");
325 return SubstringUnchecked (startIndex, this.length - startIndex);
328 public String Substring (int startIndex, int length)
331 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
333 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
334 if (startIndex > this.length)
335 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
336 if (startIndex > this.length - length)
337 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
338 if (startIndex == 0 && length == this.length)
341 return SubstringUnchecked (startIndex, length);
344 // This method is used by StringBuilder.ToString() and is expected to
345 // always create a new string object (or return String.Empty).
346 internal unsafe String SubstringUnchecked (int startIndex, int length)
351 string tmp = InternalAllocateStr (length);
352 fixed (char* dest = tmp, src = this) {
353 CharCopy (dest, src + startIndex, length);
358 private static readonly char[] WhiteChars = {
359 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
360 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
361 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
362 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
363 (char) 0x3000, (char) 0xFEFF,
366 (char) 0x202f, (char) 0x205f,
370 public String Trim ()
374 int start = FindNotWhiteSpace (0, length, 1);
379 int end = FindNotWhiteSpace (length - 1, start, -1);
381 int newLength = end - start + 1;
382 if (newLength == length)
385 return SubstringUnchecked (start, newLength);
388 public String Trim (params char[] trimChars)
390 if (trimChars == null || trimChars.Length == 0)
395 int start = FindNotInTable (0, length, 1, trimChars);
400 int end = FindNotInTable (length - 1, start, -1, trimChars);
402 int newLength = end - start + 1;
403 if (newLength == length)
406 return SubstringUnchecked (start, newLength);
409 public String TrimStart (params char[] trimChars)
414 if (trimChars == null || trimChars.Length == 0)
415 start = FindNotWhiteSpace (0, length, 1);
417 start = FindNotInTable (0, length, 1, trimChars);
422 return SubstringUnchecked (start, length - start);
425 public String TrimEnd (params char[] trimChars)
430 if (trimChars == null || trimChars.Length == 0)
431 end = FindNotWhiteSpace (length - 1, -1, -1);
433 end = FindNotInTable (length - 1, -1, -1, trimChars);
439 return SubstringUnchecked (0, end);
442 private int FindNotWhiteSpace (int pos, int target, int change)
444 while (pos != target) {
448 if (c < 0x9 || c > 0xD)
453 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
454 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029
456 // On Silverlight this whitespace participates in Trim
457 && c != 0x202f && c != 0x205f
460 if (c < 0x2000 || c > 0x200B)
469 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
471 fixed (char* tablePtr = table, thisPtr = this) {
472 while (pos != target) {
473 char c = thisPtr[pos];
475 while (x < table.Length) {
476 if (c == tablePtr[x])
480 if (x == table.Length)
488 public static int Compare (String strA, String strB)
490 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
493 public static int Compare (String strA, String strB, bool ignoreCase)
495 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
498 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
501 throw new ArgumentNullException ("culture");
503 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
506 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
508 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
511 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
513 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
516 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
519 throw new ArgumentNullException ("culture");
521 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
522 throw new ArgumentOutOfRangeException ();
534 else if (strB == null) {
538 CompareOptions compopts;
541 compopts = CompareOptions.IgnoreCase;
543 compopts = CompareOptions.None;
545 // Need to cap the requested length to the
546 // length of the string, because
547 // CompareInfo.Compare will insist that length
548 // <= (string.Length - offset)
553 if (length > (strA.Length - indexA)) {
554 len1 = strA.Length - indexA;
557 if (length > (strB.Length - indexB)) {
558 len2 = strB.Length - indexB;
561 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
562 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
565 public static int Compare (string strA, string strB, StringComparison comparisonType)
567 switch (comparisonType) {
568 case StringComparison.CurrentCulture:
569 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
570 case StringComparison.CurrentCultureIgnoreCase:
571 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
572 case StringComparison.InvariantCulture:
573 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
574 case StringComparison.InvariantCultureIgnoreCase:
575 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
576 case StringComparison.Ordinal:
577 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
578 case StringComparison.OrdinalIgnoreCase:
579 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
581 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
582 throw new ArgumentException (msg, "comparisonType");
586 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
588 switch (comparisonType) {
589 case StringComparison.CurrentCulture:
590 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
591 case StringComparison.CurrentCultureIgnoreCase:
592 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
593 case StringComparison.InvariantCulture:
594 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
595 case StringComparison.InvariantCultureIgnoreCase:
596 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
597 case StringComparison.Ordinal:
598 return CompareOrdinal (strA, indexA, strB, indexB, length);
599 case StringComparison.OrdinalIgnoreCase:
600 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
602 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
603 throw new ArgumentException (msg, "comparisonType");
607 public static bool Equals (string a, string b, StringComparison comparisonType)
609 return String.Compare (a, b, comparisonType) == 0;
612 public bool Equals (string value, StringComparison comparisonType)
614 return String.Compare (value, this, comparisonType) == 0;
617 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
620 throw new ArgumentNullException ("culture");
622 return culture.CompareInfo.Compare (strA, strB, options);
625 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
628 throw new ArgumentNullException ("culture");
633 if (length > (strA.Length - indexA))
634 len1 = strA.Length - indexA;
636 if (length > (strB.Length - indexB))
637 len2 = strB.Length - indexB;
639 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
642 public int CompareTo (Object value)
647 if (!(value is String))
648 throw new ArgumentException ();
650 return String.Compare (this, (String) value);
653 public int CompareTo (String strB)
658 return Compare (this, strB);
661 public static int CompareOrdinal (String strA, String strB)
663 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
666 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
668 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
669 throw new ArgumentOutOfRangeException ();
671 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
674 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
676 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
677 throw new ArgumentOutOfRangeException ();
679 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
682 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
689 } else if (strB == null) {
692 int lengthA = Math.Min (lenA, strA.Length - indexA);
693 int lengthB = Math.Min (lenB, strB.Length - indexB);
695 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
698 fixed (char* aptr = strA, bptr = strB) {
699 char* ap = aptr + indexA;
700 char* end = ap + Math.Min (lengthA, lengthB);
701 char* bp = bptr + indexB;
708 return lengthA - lengthB;
712 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
714 // Same as above, but checks versus uppercase characters
720 } else if (strB == null) {
723 int lengthA = Math.Min (lenA, strA.Length - indexA);
724 int lengthB = Math.Min (lenB, strB.Length - indexB);
726 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
729 fixed (char* aptr = strA, bptr = strB) {
730 char* ap = aptr + indexA;
731 char* end = ap + Math.Min (lengthA, lengthB);
732 char* bp = bptr + indexB;
735 char c1 = Char.ToUpperInvariant (*ap);
736 char c2 = Char.ToUpperInvariant (*bp);
743 return lengthA - lengthB;
747 public bool EndsWith (String value)
750 throw new ArgumentNullException ("value");
752 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
755 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
758 throw new ArgumentNullException ("value");
760 culture = CultureInfo.CurrentCulture;
762 return culture.CompareInfo.IsSuffix (this, value,
763 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
766 // Following methods are culture-insensitive
767 public int IndexOfAny (char [] anyOf)
770 throw new ArgumentNullException ();
771 if (this.length == 0)
774 return IndexOfAnyUnchecked (anyOf, 0, this.length);
777 public int IndexOfAny (char [] anyOf, int startIndex)
780 throw new ArgumentNullException ();
781 if (startIndex < 0 || startIndex > this.length)
782 throw new ArgumentOutOfRangeException ();
784 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
787 public int IndexOfAny (char [] anyOf, int startIndex, int count)
790 throw new ArgumentNullException ();
791 if (startIndex < 0 || startIndex > this.length)
792 throw new ArgumentOutOfRangeException ();
793 if (count < 0 || startIndex > this.length - count)
794 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
796 return IndexOfAnyUnchecked (anyOf, startIndex, count);
799 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
801 if (anyOf.Length == 0)
804 if (anyOf.Length == 1)
805 return IndexOfUnchecked (anyOf[0], startIndex, count);
807 fixed (char* any = anyOf) {
811 char* end_any_ptr = any + anyOf.Length;
813 while (++any_ptr != end_any_ptr) {
814 if (*any_ptr > highest) {
819 if (*any_ptr < lowest)
823 fixed (char* start = &start_char) {
824 char* ptr = start + startIndex;
825 char* end_ptr = ptr + count;
827 while (ptr != end_ptr) {
828 if (*ptr > highest || *ptr < lowest) {
834 return (int)(ptr - start);
837 while (++any_ptr != end_any_ptr) {
838 if (*ptr == *any_ptr)
839 return (int)(ptr - start);
850 public int IndexOf (string value, StringComparison comparisonType)
852 return IndexOf (value, 0, this.Length, comparisonType);
855 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
857 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
860 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
862 switch (comparisonType) {
863 case StringComparison.CurrentCulture:
864 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
865 case StringComparison.CurrentCultureIgnoreCase:
866 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
867 case StringComparison.InvariantCulture:
868 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
869 case StringComparison.InvariantCultureIgnoreCase:
870 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
871 case StringComparison.Ordinal:
872 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
873 case StringComparison.OrdinalIgnoreCase:
874 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
876 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
877 throw new ArgumentException (msg, "comparisonType");
881 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
884 throw new ArgumentNullException ("value");
886 throw new ArgumentOutOfRangeException ("startIndex");
887 if (count < 0 || (this.length - startIndex) < count)
888 throw new ArgumentOutOfRangeException ("count");
890 if (options == CompareOptions.Ordinal)
891 return IndexOfOrdinalUnchecked (value, startIndex, count);
892 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
895 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
897 int valueLen = value.Length;
898 if (count < valueLen)
903 return IndexOfUnchecked (value[0], startIndex, count);
907 fixed (char* thisptr = this, valueptr = value) {
908 char* ap = thisptr + startIndex;
909 char* thisEnd = ap + count - valueLen + 1;
910 while (ap != thisEnd) {
911 if (*ap == *valueptr) {
912 for (int i = 1; i < valueLen; i++) {
913 if (ap[i] != valueptr[i])
916 return (int)(ap - thisptr);
925 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
927 int valueLen = value.Length;
928 if (count < valueLen)
934 fixed (char* thisptr = this, valueptr = value) {
935 char* ap = thisptr + startIndex;
936 char* thisEnd = ap + count - valueLen + 1;
937 while (ap != thisEnd) {
938 for (int i = 0; i < valueLen; i++) {
939 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
942 return (int)(ap - thisptr);
950 public int LastIndexOf (string value, StringComparison comparisonType)
952 if (this.Length == 0)
953 return value == String.Empty ? 0 : -1;
955 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
958 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
960 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
963 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
965 switch (comparisonType) {
966 case StringComparison.CurrentCulture:
967 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
968 case StringComparison.CurrentCultureIgnoreCase:
969 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
970 case StringComparison.InvariantCulture:
971 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
972 case StringComparison.InvariantCultureIgnoreCase:
973 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
974 case StringComparison.Ordinal:
975 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
976 case StringComparison.OrdinalIgnoreCase:
977 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
979 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
980 throw new ArgumentException (msg, "comparisonType");
984 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
987 throw new ArgumentNullException ("value");
988 if (this.Length == 0)
989 return value == String.Empty ? 0 : -1;
990 if (value.Length == 0)
991 return Math.Min (this.Length - 1, startIndex);
992 if (startIndex < 0 || startIndex > length)
993 throw new ArgumentOutOfRangeException ("startIndex");
994 if (count < 0 || (startIndex < count - 1))
995 throw new ArgumentOutOfRangeException ("count");
997 if (options == CompareOptions.Ordinal)
998 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
999 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1002 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1004 int valueLen = value.Length;
1005 if (count < valueLen)
1008 if (valueLen <= 1) {
1010 return LastIndexOfUnchecked (value[0], startIndex, count);
1014 fixed (char* thisptr = this, valueptr = value) {
1015 char* ap = thisptr + startIndex - valueLen + 1;
1016 char* thisEnd = ap - count + valueLen - 1;
1017 while (ap != thisEnd) {
1018 if (*ap == *valueptr) {
1019 for (int i = 1; i < valueLen; i++) {
1020 if (ap[i] != valueptr[i])
1023 return (int)(ap - thisptr);
1032 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1034 int valueLen = value.Length;
1035 if (count < valueLen)
1041 fixed (char* thisptr = this, valueptr = value) {
1042 char* ap = thisptr + startIndex - valueLen + 1;
1043 char* thisEnd = ap - count + valueLen - 1;
1044 while (ap != thisEnd) {
1045 for (int i = 0; i < valueLen; i++) {
1046 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1049 return (int)(ap - thisptr);
1057 // Following methods are culture-insensitive
1058 public int IndexOf (char value)
1060 if (this.length == 0)
1063 return IndexOfUnchecked (value, 0, this.length);
1066 public int IndexOf (char value, int startIndex)
1069 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1070 if (startIndex > this.length)
1071 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1073 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1076 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1079 public int IndexOf (char value, int startIndex, int count)
1081 if (startIndex < 0 || startIndex > this.length)
1082 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1084 throw new ArgumentOutOfRangeException ("count", "< 0");
1085 if (startIndex > this.length - count)
1086 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1088 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1091 return IndexOfUnchecked (value, startIndex, count);
1094 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1096 // It helps JIT compiler to optimize comparison
1097 int value_32 = (int)value;
1099 fixed (char* start = &start_char) {
1100 char* ptr = start + startIndex;
1101 char* end_ptr = ptr + (count >> 3 << 3);
1103 while (ptr != end_ptr) {
1104 if (*ptr == value_32)
1105 return (int)(ptr - start);
1106 if (ptr[1] == value_32)
1107 return (int)(ptr - start + 1);
1108 if (ptr[2] == value_32)
1109 return (int)(ptr - start + 2);
1110 if (ptr[3] == value_32)
1111 return (int)(ptr - start + 3);
1112 if (ptr[4] == value_32)
1113 return (int)(ptr - start + 4);
1114 if (ptr[5] == value_32)
1115 return (int)(ptr - start + 5);
1116 if (ptr[6] == value_32)
1117 return (int)(ptr - start + 6);
1118 if (ptr[7] == value_32)
1119 return (int)(ptr - start + 7);
1124 end_ptr += count & 0x07;
1125 while (ptr != end_ptr) {
1126 if (*ptr == value_32)
1127 return (int)(ptr - start);
1135 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1139 int end = startIndex + count;
1140 char c = Char.ToUpperInvariant (value);
1141 fixed (char* s = &start_char) {
1142 for (int i = startIndex; i < end; i++)
1143 if (Char.ToUpperInvariant (s [i]) == c)
1149 // Following methods are culture-sensitive
1150 public int IndexOf (String value)
1153 throw new ArgumentNullException ("value");
1154 if (value.length == 0)
1156 if (this.length == 0)
1158 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.Ordinal);
1161 public int IndexOf (String value, int startIndex)
1163 return IndexOf (value, startIndex, this.length - startIndex);
1166 public int IndexOf (String value, int startIndex, int count)
1169 throw new ArgumentNullException ("value");
1170 if (startIndex < 0 || startIndex > this.length)
1171 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1172 if (count < 0 || startIndex > this.length - count)
1173 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1175 if (value.length == 0)
1178 if (startIndex == 0 && this.length == 0)
1184 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1187 // Following methods are culture-insensitive
1188 public int LastIndexOfAny (char [] anyOf)
1191 throw new ArgumentNullException ();
1192 if (this.length == 0)
1195 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1198 public int LastIndexOfAny (char [] anyOf, int startIndex)
1201 throw new ArgumentNullException ();
1202 if (this.length == 0)
1205 if (startIndex < 0 || startIndex >= this.length)
1206 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1208 if (this.length == 0)
1211 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1214 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1217 throw new ArgumentNullException ();
1218 if (this.length == 0)
1221 if ((startIndex < 0) || (startIndex >= this.Length))
1222 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1223 if ((count < 0) || (count > this.Length))
1224 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1225 if (startIndex - count + 1 < 0)
1226 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1228 if (this.length == 0)
1231 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1234 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1236 if (anyOf.Length == 1)
1237 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1239 fixed (char* start = this, testStart = anyOf) {
1240 char* ptr = start + startIndex;
1241 char* ptrEnd = ptr - count;
1243 char* testEnd = testStart + anyOf.Length;
1245 while (ptr != ptrEnd) {
1247 while (test != testEnd) {
1249 return (int)(ptr - start);
1258 // Following methods are culture-insensitive
1259 public int LastIndexOf (char value)
1261 if (this.length == 0)
1264 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1267 public int LastIndexOf (char value, int startIndex)
1269 return LastIndexOf (value, startIndex, startIndex + 1);
1272 public int LastIndexOf (char value, int startIndex, int count)
1274 if (this.length == 0)
1277 // >= for char (> for string)
1278 if ((startIndex < 0) || (startIndex >= this.Length))
1279 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1280 if ((count < 0) || (count > this.Length))
1281 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1282 if (startIndex - count + 1 < 0)
1283 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1285 return LastIndexOfUnchecked (value, startIndex, count);
1288 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1290 // It helps JIT compiler to optimize comparison
1291 int value_32 = (int)value;
1293 fixed (char* start = &start_char) {
1294 char* ptr = start + startIndex;
1295 char* end_ptr = ptr - (count >> 3 << 3);
1297 while (ptr != end_ptr) {
1298 if (*ptr == value_32)
1299 return (int)(ptr - start);
1300 if (ptr[-1] == value_32)
1301 return (int)(ptr - start) - 1;
1302 if (ptr[-2] == value_32)
1303 return (int)(ptr - start) - 2;
1304 if (ptr[-3] == value_32)
1305 return (int)(ptr - start) - 3;
1306 if (ptr[-4] == value_32)
1307 return (int)(ptr - start) - 4;
1308 if (ptr[-5] == value_32)
1309 return (int)(ptr - start) - 5;
1310 if (ptr[-6] == value_32)
1311 return (int)(ptr - start) - 6;
1312 if (ptr[-7] == value_32)
1313 return (int)(ptr - start) - 7;
1318 end_ptr -= count & 0x07;
1319 while (ptr != end_ptr) {
1320 if (*ptr == value_32)
1321 return (int)(ptr - start);
1329 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1333 int end = startIndex - count;
1334 char c = Char.ToUpperInvariant (value);
1335 fixed (char* s = &start_char) {
1336 for (int i = startIndex; i > end; i--)
1337 if (Char.ToUpperInvariant (s [i]) == c)
1343 // Following methods are culture-sensitive
1344 public int LastIndexOf (String value)
1346 return LastIndexOf (value, this.length - 1, this.length);
1349 public int LastIndexOf (String value, int startIndex)
1351 int max = startIndex;
1352 if (max < this.Length)
1354 return LastIndexOf (value, startIndex, max);
1357 public int LastIndexOf (String value, int startIndex, int count)
1360 throw new ArgumentNullException ("value");
1362 if (this.length == 0)
1363 return value == String.Empty ? 0 : -1;
1364 // -1 > startIndex > for string (0 > startIndex >= for char)
1365 if ((startIndex < -1) || (startIndex > this.Length))
1366 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1367 if ((count < 0) || (count > this.Length))
1368 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1369 if (startIndex - count + 1 < 0)
1370 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1372 if (value.Length == 0)
1373 return Math.Min (this.Length - 1, startIndex);
1375 if (startIndex == 0 && this.length == 0)
1378 // This check is needed to match undocumented MS behaviour
1379 if (this.length == 0 && value.length > 0)
1385 if (startIndex == this.Length)
1387 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1390 public bool Contains (String value)
1392 return IndexOf (value) != -1;
1395 public static bool IsNullOrEmpty (String value)
1397 return (value == null) || (value.Length == 0);
1401 public string Normalize ()
1403 return Normalization.Normalize (this, 0);
1406 public string Normalize (NormalizationForm normalizationForm)
1408 switch (normalizationForm) {
1410 return Normalization.Normalize (this, 0);
1411 case NormalizationForm.FormD:
1412 return Normalization.Normalize (this, 1);
1413 case NormalizationForm.FormKC:
1414 return Normalization.Normalize (this, 2);
1415 case NormalizationForm.FormKD:
1416 return Normalization.Normalize (this, 3);
1420 public bool IsNormalized ()
1422 return Normalization.IsNormalized (this, 0);
1425 public bool IsNormalized (NormalizationForm normalizationForm)
1427 switch (normalizationForm) {
1429 return Normalization.IsNormalized (this, 0);
1430 case NormalizationForm.FormD:
1431 return Normalization.IsNormalized (this, 1);
1432 case NormalizationForm.FormKC:
1433 return Normalization.IsNormalized (this, 2);
1434 case NormalizationForm.FormKD:
1435 return Normalization.IsNormalized (this, 3);
1440 public string Remove (int startIndex)
1443 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1444 if (startIndex >= this.length)
1445 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1447 return Remove (startIndex, this.length - startIndex);
1450 public String PadLeft (int totalWidth)
1452 return PadLeft (totalWidth, ' ');
1455 public unsafe String PadLeft (int totalWidth, char paddingChar)
1457 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1460 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1462 if (totalWidth < this.length)
1465 String tmp = InternalAllocateStr (totalWidth);
1467 fixed (char* dest = tmp, src = this) {
1468 char* padPos = dest;
1469 char* padTo = dest + (totalWidth - length);
1470 while (padPos != padTo)
1471 *padPos++ = paddingChar;
1473 CharCopy (padTo, src, length);
1478 public String PadRight (int totalWidth)
1480 return PadRight (totalWidth, ' ');
1483 public unsafe String PadRight (int totalWidth, char paddingChar)
1485 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1488 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1490 if (totalWidth < this.length)
1492 if (totalWidth == 0)
1493 return String.Empty;
1495 String tmp = InternalAllocateStr (totalWidth);
1497 fixed (char* dest = tmp, src = this) {
1498 CharCopy (dest, src, length);
1500 char* padPos = dest + length;
1501 char* padTo = dest + totalWidth;
1502 while (padPos != padTo)
1503 *padPos++ = paddingChar;
1508 public bool StartsWith (String value)
1511 throw new ArgumentNullException ("value");
1513 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1516 [ComVisible (false)]
1517 public bool StartsWith (string value, StringComparison comparisonType)
1520 throw new ArgumentNullException ("value");
1522 switch (comparisonType) {
1523 case StringComparison.CurrentCulture:
1524 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1525 case StringComparison.CurrentCultureIgnoreCase:
1526 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1527 case StringComparison.InvariantCulture:
1528 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1529 case StringComparison.InvariantCultureIgnoreCase:
1530 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1531 case StringComparison.Ordinal:
1532 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1533 case StringComparison.OrdinalIgnoreCase:
1534 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1536 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1537 throw new ArgumentException (msg, "comparisonType");
1541 [ComVisible (false)]
1542 public bool EndsWith (string value, StringComparison comparisonType)
1545 throw new ArgumentNullException ("value");
1547 switch (comparisonType) {
1548 case StringComparison.CurrentCulture:
1549 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1550 case StringComparison.CurrentCultureIgnoreCase:
1551 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1552 case StringComparison.InvariantCulture:
1553 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1554 case StringComparison.InvariantCultureIgnoreCase:
1555 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1556 case StringComparison.Ordinal:
1557 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1558 case StringComparison.OrdinalIgnoreCase:
1559 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1561 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1562 throw new ArgumentException (msg, "comparisonType");
1566 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1568 if (culture == null)
1569 culture = CultureInfo.CurrentCulture;
1571 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1574 // Following method is culture-insensitive
1575 public unsafe String Replace (char oldChar, char newChar)
1577 if (this.length == 0 || oldChar == newChar)
1580 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1581 if (start_pos == -1)
1587 string tmp = InternalAllocateStr (length);
1588 fixed (char* dest = tmp, src = &start_char) {
1590 CharCopy (dest, src, start_pos);
1592 char* end_ptr = dest + length;
1593 char* dest_ptr = dest + start_pos;
1594 char* src_ptr = src + start_pos;
1596 while (dest_ptr != end_ptr) {
1597 if (*src_ptr == oldChar)
1598 *dest_ptr = newChar;
1600 *dest_ptr = *src_ptr;
1609 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1610 public String Replace (String oldValue, String newValue)
1612 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1613 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1615 if (oldValue == null)
1616 throw new ArgumentNullException ("oldValue");
1618 if (oldValue.Length == 0)
1619 throw new ArgumentException ("oldValue is the empty string.");
1621 if (this.Length == 0)
1624 if (newValue == null)
1625 newValue = String.Empty;
1627 return ReplaceUnchecked (oldValue, newValue);
1630 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1632 if (oldValue.length > length)
1634 if (oldValue.length == 1 && newValue.length == 1) {
1635 return Replace (oldValue[0], newValue[0]);
1636 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1637 // because the length of the result would be this.length and length calculation unneccesary
1640 const int maxValue = 200; // Allocate 800 byte maximum
1641 int* dat = stackalloc int[maxValue];
1642 fixed (char* source = this, replace = newValue) {
1643 int i = 0, count = 0;
1644 while (i < length) {
1645 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1649 if (count < maxValue)
1650 dat[count++] = found;
1652 return ReplaceFallback (oldValue, newValue, maxValue);
1654 i = found + oldValue.length;
1658 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1659 String tmp = InternalAllocateStr (nlen);
1661 int curPos = 0, lastReadPos = 0;
1662 fixed (char* dest = tmp) {
1663 for (int j = 0; j < count; j++) {
1664 int precopy = dat[j] - lastReadPos;
1665 CharCopy (dest + curPos, source + lastReadPos, precopy);
1667 lastReadPos = dat[j] + oldValue.length;
1668 CharCopy (dest + curPos, replace, newValue.length);
1669 curPos += newValue.length;
1671 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1677 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1679 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1680 StringBuilder sb = new StringBuilder (lengthEstimate);
1681 for (int i = 0; i < length;) {
1682 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1684 sb.Append (SubstringUnchecked (i, length - i));
1687 sb.Append (SubstringUnchecked (i, found - i));
1688 sb.Append (newValue);
1689 i = found + oldValue.Length;
1691 return sb.ToString ();
1695 public unsafe String Remove (int startIndex, int count)
1698 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1700 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1701 if (startIndex > this.length - count)
1702 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1704 String tmp = InternalAllocateStr (this.length - count);
1706 fixed (char *dest = tmp, src = this) {
1708 CharCopy (dst, src, startIndex);
1709 int skip = startIndex + count;
1711 CharCopy (dst, src + skip, length - skip);
1716 public String ToLower ()
1718 return ToLower (CultureInfo.CurrentCulture);
1721 public String ToLower (CultureInfo culture)
1723 if (culture == null)
1724 throw new ArgumentNullException ("culture");
1726 if (culture.LCID == 0x007F) // Invariant
1727 return ToLowerInvariant ();
1729 return culture.TextInfo.ToLower (this);
1732 public unsafe String ToLowerInvariant ()
1735 return String.Empty;
1737 string tmp = InternalAllocateStr (length);
1738 fixed (char* source = &start_char, dest = tmp) {
1740 char* destPtr = (char*)dest;
1741 char* sourcePtr = (char*)source;
1743 for (int n = 0; n < length; n++) {
1744 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1752 public String ToUpper ()
1754 return ToUpper (CultureInfo.CurrentCulture);
1757 public String ToUpper (CultureInfo culture)
1759 if (culture == null)
1760 throw new ArgumentNullException ("culture");
1762 if (culture.LCID == 0x007F) // Invariant
1763 return ToUpperInvariant ();
1765 return culture.TextInfo.ToUpper (this);
1768 public unsafe String ToUpperInvariant ()
1771 return String.Empty;
1773 string tmp = InternalAllocateStr (length);
1774 fixed (char* source = &start_char, dest = tmp) {
1776 char* destPtr = (char*)dest;
1777 char* sourcePtr = (char*)source;
1779 for (int n = 0; n < length; n++) {
1780 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1788 public override String ToString ()
1793 public String ToString (IFormatProvider provider)
1798 public static String Format (String format, Object arg0)
1800 return Format (null, format, new Object[] {arg0});
1803 public static String Format (String format, Object arg0, Object arg1)
1805 return Format (null, format, new Object[] {arg0, arg1});
1808 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1810 return Format (null, format, new Object[] {arg0, arg1, arg2});
1813 public static string Format (string format, params object[] args)
1815 return Format (null, format, args);
1818 public static string Format (IFormatProvider provider, string format, params object[] args)
1820 StringBuilder b = FormatHelper (null, provider, format, args);
1821 return b.ToString ();
1824 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1827 throw new ArgumentNullException ("format");
1829 throw new ArgumentNullException ("args");
1831 if (result == null) {
1832 /* Try to approximate the size of result to avoid reallocations */
1836 for (i = 0; i < args.Length; ++i) {
1837 string s = args [i] as string;
1843 if (i == args.Length)
1844 result = new StringBuilder (len + format.length);
1846 result = new StringBuilder ();
1851 while (ptr < format.length) {
1852 char c = format[ptr ++];
1855 result.Append (format, start, ptr - start - 1);
1857 // check for escaped open bracket
1859 if (format[ptr] == '{') {
1870 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1871 if (n >= args.Length)
1872 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1876 object arg = args[n];
1879 ICustomFormatter formatter = null;
1880 if (provider != null)
1881 formatter = provider.GetFormat (typeof (ICustomFormatter))
1882 as ICustomFormatter;
1885 else if (formatter != null)
1886 str = formatter.Format (arg_format, arg, provider);
1887 else if (arg is IFormattable)
1888 str = ((IFormattable)arg).ToString (arg_format, provider);
1890 str = arg.ToString ();
1892 // pad formatted string and append to result
1894 if (width > str.length) {
1895 const char padchar = ' ';
1896 int padlen = width - str.length;
1899 result.Append (str);
1900 result.Append (padchar, padlen);
1903 result.Append (padchar, padlen);
1904 result.Append (str);
1908 result.Append (str);
1912 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1913 result.Append (format, start, ptr - start - 1);
1916 else if (c == '}') {
1917 throw new FormatException ("Input string was not in a correct format.");
1921 if (start < format.length)
1922 result.Append (format, start, format.Length - start);
1927 public unsafe static String Copy (String str)
1930 throw new ArgumentNullException ("str");
1932 int length = str.length;
1934 String tmp = InternalAllocateStr (length);
1936 fixed (char *dest = tmp, src = str) {
1937 CharCopy (dest, src, length);
1943 public static String Concat (Object arg0)
1946 return String.Empty;
1948 return arg0.ToString ();
1951 public static String Concat (Object arg0, Object arg1)
1953 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
1956 public static String Concat (Object arg0, Object arg1, Object arg2)
1962 s1 = arg0.ToString ();
1967 s2 = arg1.ToString ();
1972 s3 = arg2.ToString ();
1974 return Concat (s1, s2, s3);
1977 [CLSCompliant(false)]
1978 public static String Concat (Object arg0, Object arg1, Object arg2,
1979 Object arg3, __arglist)
1981 string s1, s2, s3, s4;
1986 s1 = arg0.ToString ();
1991 s2 = arg1.ToString ();
1996 s3 = arg2.ToString ();
1998 ArgIterator iter = new ArgIterator (__arglist);
1999 int argCount = iter.GetRemainingCount();
2001 StringBuilder sb = new StringBuilder ();
2003 sb.Append (arg3.ToString ());
2005 for (int i = 0; i < argCount; i++) {
2006 TypedReference typedRef = iter.GetNextArg ();
2007 sb.Append (TypedReference.ToObject (typedRef));
2010 s4 = sb.ToString ();
2012 return Concat (s1, s2, s3, s4);
2015 public unsafe static String Concat (String str0, String str1)
2017 if (str0 == null || str0.Length == 0) {
2018 if (str1 == null || str1.Length == 0)
2019 return String.Empty;
2023 if (str1 == null || str1.Length == 0)
2026 String tmp = InternalAllocateStr (str0.length + str1.length);
2028 fixed (char *dest = tmp, src = str0)
2029 CharCopy (dest, src, str0.length);
2030 fixed (char *dest = tmp, src = str1)
2031 CharCopy (dest + str0.Length, src, str1.length);
2036 public unsafe static String Concat (String str0, String str1, String str2)
2038 if (str0 == null || str0.Length == 0){
2039 if (str1 == null || str1.Length == 0){
2040 if (str2 == null || str2.Length == 0)
2041 return String.Empty;
2044 if (str2 == null || str2.Length == 0)
2047 str0 = String.Empty;
2049 if (str1 == null || str1.Length == 0){
2050 if (str2 == null || str2.Length == 0)
2053 str1 = String.Empty;
2055 if (str2 == null || str2.Length == 0)
2056 str2 = String.Empty;
2060 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2062 if (str0.Length != 0) {
2063 fixed (char *dest = tmp, src = str0) {
2064 CharCopy (dest, src, str0.length);
2067 if (str1.Length != 0) {
2068 fixed (char *dest = tmp, src = str1) {
2069 CharCopy (dest + str0.Length, src, str1.length);
2072 if (str2.Length != 0) {
2073 fixed (char *dest = tmp, src = str2) {
2074 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2081 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2083 if (str0 == null && str1 == null && str2 == null && str3 == null)
2084 return String.Empty;
2087 str0 = String.Empty;
2089 str1 = String.Empty;
2091 str2 = String.Empty;
2093 str3 = String.Empty;
2095 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2097 if (str0.Length != 0) {
2098 fixed (char *dest = tmp, src = str0) {
2099 CharCopy (dest, src, str0.length);
2102 if (str1.Length != 0) {
2103 fixed (char *dest = tmp, src = str1) {
2104 CharCopy (dest + str0.Length, src, str1.length);
2107 if (str2.Length != 0) {
2108 fixed (char *dest = tmp, src = str2) {
2109 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2112 if (str3.Length != 0) {
2113 fixed (char *dest = tmp, src = str3) {
2114 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2121 public static String Concat (params Object[] args)
2124 throw new ArgumentNullException ("args");
2126 int argLen = args.Length;
2128 return String.Empty;
2130 string [] strings = new string [argLen];
2132 for (int i = 0; i < argLen; i++) {
2133 if (args[i] != null) {
2134 strings[i] = args[i].ToString ();
2135 len += strings[i].length;
2139 return ConcatInternal (strings, len);
2142 public static String Concat (params String[] values)
2145 throw new ArgumentNullException ("values");
2148 for (int i = 0; i < values.Length; i++) {
2149 String s = values[i];
2154 return ConcatInternal (values, len);
2157 private static unsafe String ConcatInternal (String[] values, int length)
2160 return String.Empty;
2162 String tmp = InternalAllocateStr (length);
2164 fixed (char* dest = tmp) {
2166 for (int i = 0; i < values.Length; i++) {
2167 String source = values[i];
2168 if (source != null) {
2169 fixed (char* src = source) {
2170 CharCopy (dest + pos, src, source.length);
2172 pos += source.Length;
2179 public unsafe String Insert (int startIndex, String value)
2182 throw new ArgumentNullException ("value");
2184 if (startIndex < 0 || startIndex > this.length)
2185 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2187 if (value.Length == 0)
2189 if (this.Length == 0)
2191 String tmp = InternalAllocateStr (this.length + value.length);
2193 fixed (char *dest = tmp, src = this, val = value) {
2195 CharCopy (dst, src, startIndex);
2197 CharCopy (dst, val, value.length);
2198 dst += value.length;
2199 CharCopy (dst, src + startIndex, length - startIndex);
2204 public static string Intern (string str)
2207 throw new ArgumentNullException ("str");
2209 return InternalIntern (str);
2212 public static string IsInterned (string str)
2215 throw new ArgumentNullException ("str");
2217 return InternalIsInterned (str);
2220 public static string Join (string separator, params string [] value)
2223 throw new ArgumentNullException ("value");
2224 if (separator == null)
2225 separator = String.Empty;
2227 return JoinUnchecked (separator, value, 0, value.Length);
2230 public static string Join (string separator, string[] value, int startIndex, int count)
2233 throw new ArgumentNullException ("value");
2235 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2237 throw new ArgumentOutOfRangeException ("count", "< 0");
2238 if (startIndex > value.Length - count)
2239 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2241 if (startIndex == value.Length)
2242 return String.Empty;
2243 if (separator == null)
2244 separator = String.Empty;
2246 return JoinUnchecked (separator, value, startIndex, count);
2249 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2251 // Unchecked parameters
2252 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2253 // separator and value must not be null
2256 int maxIndex = startIndex + count;
2257 // Precount the number of characters that the resulting string will have
2258 for (int i = startIndex; i < maxIndex; i++) {
2259 String s = value[i];
2263 length += separator.length * (count - 1);
2265 return String.Empty;
2267 String tmp = InternalAllocateStr (length);
2270 fixed (char* dest = tmp, sepsrc = separator) {
2271 // Copy each string from value except the last one and add a separator for each
2273 for (int i = startIndex; i < maxIndex; i++) {
2274 String source = value[i];
2275 if (source != null) {
2276 if (source.Length > 0) {
2277 fixed (char* src = source)
2278 CharCopy (dest + pos, src, source.Length);
2279 pos += source.Length;
2282 if (separator.Length > 0) {
2283 CharCopy (dest + pos, sepsrc, separator.Length);
2284 pos += separator.Length;
2287 // Append last string that does not get an additional separator
2288 String sourceLast = value[maxIndex];
2289 if (sourceLast != null) {
2290 if (sourceLast.Length > 0) {
2291 fixed (char* src = sourceLast)
2292 CharCopy (dest + pos, src, sourceLast.Length);
2299 bool IConvertible.ToBoolean (IFormatProvider provider)
2301 return Convert.ToBoolean (this, provider);
2304 byte IConvertible.ToByte (IFormatProvider provider)
2306 return Convert.ToByte (this, provider);
2309 char IConvertible.ToChar (IFormatProvider provider)
2311 return Convert.ToChar (this, provider);
2314 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2316 return Convert.ToDateTime (this, provider);
2319 decimal IConvertible.ToDecimal (IFormatProvider provider)
2321 return Convert.ToDecimal (this, provider);
2324 double IConvertible.ToDouble (IFormatProvider provider)
2326 return Convert.ToDouble (this, provider);
2329 short IConvertible.ToInt16 (IFormatProvider provider)
2331 return Convert.ToInt16 (this, provider);
2334 int IConvertible.ToInt32 (IFormatProvider provider)
2336 return Convert.ToInt32 (this, provider);
2339 long IConvertible.ToInt64 (IFormatProvider provider)
2341 return Convert.ToInt64 (this, provider);
2344 sbyte IConvertible.ToSByte (IFormatProvider provider)
2346 return Convert.ToSByte (this, provider);
2349 float IConvertible.ToSingle (IFormatProvider provider)
2351 return Convert.ToSingle (this, provider);
2354 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2356 if (targetType == null)
2357 throw new ArgumentNullException ("type");
2358 return Convert.ToType (this, targetType, provider, false);
2361 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2363 return Convert.ToUInt16 (this, provider);
2366 uint IConvertible.ToUInt32 (IFormatProvider provider)
2368 return Convert.ToUInt32 (this, provider);
2371 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2373 return Convert.ToUInt64 (this, provider);
2382 public CharEnumerator GetEnumerator ()
2384 return new CharEnumerator (this);
2387 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2389 return new CharEnumerator (this);
2392 IEnumerator IEnumerable.GetEnumerator ()
2394 return new CharEnumerator (this);
2397 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2398 out bool left_align, out string format)
2400 int max = str.Length;
2402 // parses format specifier of form:
2406 // N = argument number (non-negative integer)
2408 n = ParseDecimal (str, ref ptr);
2410 throw new FormatException ("Input string was not in a correct format.");
2412 // M = width (non-negative integer)
2414 if (ptr < max && str[ptr] == ',') {
2415 // White space between ',' and number or sign.
2417 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2421 format = str.Substring (start, ptr - start);
2423 left_align = (ptr < max && str [ptr] == '-');
2427 width = ParseDecimal (str, ref ptr);
2429 throw new FormatException ("Input string was not in a correct format.");
2434 format = String.Empty;
2437 // F = argument format (string)
2439 if (ptr < max && str[ptr] == ':') {
2441 while (ptr < max && str[ptr] != '}')
2444 format += str.Substring (start, ptr - start);
2449 if ((ptr >= max) || str[ptr ++] != '}')
2450 throw new FormatException ("Input string was not in a correct format.");
2453 private static int ParseDecimal (string str, ref int ptr)
2457 int max = str.Length;
2461 if (c < '0' || '9' < c)
2464 n = n * 10 + c - '0';
2468 if (p == ptr || p == max)
2475 internal unsafe void InternalSetChar (int idx, char val)
2477 if ((uint) idx >= (uint) Length)
2478 throw new ArgumentOutOfRangeException ("idx");
2480 fixed (char * pStr = &start_char)
2486 internal unsafe void InternalSetLength (int newLength)
2488 if (newLength > length)
2489 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2491 // zero terminate, we can pass string objects directly via pinvoke
2492 // we also zero the rest of the string, since the new GC needs to be
2493 // able to handle the changing size (it will skip the 0 bytes).
2494 fixed (char * pStr = &start_char) {
2495 char *p = pStr + newLength;
2496 char *end = pStr + length;
2505 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2506 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2507 public unsafe override int GetHashCode ()
2509 fixed (char * c = this) {
2511 char * end = cc + length - 1;
2513 for (;cc < end; cc += 2) {
2514 h = (h << 5) - h + *cc;
2515 h = (h << 5) - h + cc [1];
2519 h = (h << 5) - h + *cc;
2524 #if MOONLIGHT || NET_4_0
2526 public static string Concat (IEnumerable<string> values)
2529 throw new ArgumentNullException ("values");
2531 var stringList = new List<string> ();
2533 foreach (var v in values){
2539 return ConcatInternal (stringList.ToArray (), len);
2542 [ComVisibleAttribute(false)]
2543 public static string Concat<T> (IEnumerable<T> values)
2546 throw new ArgumentNullException ("values");
2548 var stringList = new List<string> ();
2550 foreach (var v in values){
2551 string sr = v.ToString ();
2553 stringList.Add (sr);
2555 return ConcatInternal (stringList.ToArray (), len);
2558 [ComVisibleAttribute(false)]
2559 public static string Join (string separator, IEnumerable<string> values)
2561 if (separator == null)
2562 return Concat (values);
2565 throw new ArgumentNullException ("values");
2567 var stringList = new List<string> ();
2568 foreach (var v in values)
2571 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2574 [ComVisibleAttribute(false)]
2575 public static string Join (string separator, params object [] values)
2577 if (separator == null)
2578 return Concat (values);
2581 throw new ArgumentNullException ("values");
2583 var strCopy = new string [values.Length];
2585 foreach (var v in values)
2586 strCopy [i++] = v.ToString ();
2588 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2591 [ComVisible (false)]
2592 public static string Join<T> (string separator, IEnumerable<T> values)
2594 if (separator == null)
2595 return Concat<T> (values);
2598 throw new ArgumentNullException ("values");
2600 var stringList = new List<string> ();
2601 foreach (var v in values)
2602 stringList.Add (v.ToString ());
2604 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2607 public static bool IsNullOrWhiteSpace (string value)
2609 internal static bool IsNullOrWhiteSpace (string value)
2612 if ((value == null) || (value.Length == 0))
2614 foreach (char c in value)
2615 if (!Char.IsWhiteSpace (c))
2620 internal unsafe int GetCaseInsensitiveHashCode ()
2622 fixed (char * c = this) {
2624 char * end = cc + length - 1;
2626 for (;cc < end; cc += 2) {
2627 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2628 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2632 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2637 // Certain constructors are redirected to CreateString methods with
2638 // matching argument list. The this pointer should not be used.
2639 #pragma warning disable 169
2640 private unsafe String CreateString (sbyte* value)
2643 return String.Empty;
2645 byte* bytes = (byte*) value;
2649 while (bytes++ [0] != 0)
2651 } catch (NullReferenceException) {
2652 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2653 } catch (AccessViolationException) {
2654 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2657 return CreateString (value, 0, length, null);
2660 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2662 return CreateString (value, startIndex, length, null);
2665 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2668 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2670 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2671 if (value + startIndex < value)
2672 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2674 bool isDefaultEncoding;
2676 if (isDefaultEncoding = (enc == null)) {
2678 throw new ArgumentNullException ("value");
2680 return String.Empty;
2682 enc = Encoding.Default;
2685 byte [] bytes = new byte [length];
2688 fixed (byte* bytePtr = bytes)
2690 memcpy (bytePtr, (byte*) (value + startIndex), length);
2691 } catch (NullReferenceException) {
2692 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2693 } catch (AccessViolationException) {
2694 if (!isDefaultEncoding)
2697 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2700 // GetString () is called even when length == 0
2701 return enc.GetString (bytes);
2704 unsafe string CreateString (char *value)
2707 return string.Empty;
2714 string result = InternalAllocateStr (i);
2717 fixed (char *dest = result) {
2718 CharCopy (dest, value, i);
2724 unsafe string CreateString (char *value, int startIndex, int length)
2727 return string.Empty;
2729 throw new ArgumentNullException ("value");
2731 throw new ArgumentOutOfRangeException ("startIndex");
2733 throw new ArgumentOutOfRangeException ("length");
2735 string result = InternalAllocateStr (length);
2737 fixed (char *dest = result) {
2738 CharCopy (dest, value + startIndex, length);
2743 unsafe string CreateString (char [] val, int startIndex, int length)
2746 throw new ArgumentNullException ("value");
2748 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2750 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2751 if (startIndex > val.Length - length)
2752 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2754 return string.Empty;
2756 string result = InternalAllocateStr (length);
2758 fixed (char *dest = result, src = val) {
2759 CharCopy (dest, src + startIndex, length);
2764 unsafe string CreateString (char [] val)
2767 return string.Empty;
2768 if (val.Length == 0)
2769 return string.Empty;
2770 string result = InternalAllocateStr (val.Length);
2772 fixed (char *dest = result, src = val) {
2773 CharCopy (dest, src, val.Length);
2778 unsafe string CreateString (char c, int count)
2781 throw new ArgumentOutOfRangeException ("count");
2783 return string.Empty;
2784 string result = InternalAllocateStr (count);
2785 fixed (char *dest = result) {
2787 char *end = p + count;
2795 #pragma warning restore 169
2797 /* helpers used by the runtime as well as above or eslewhere in corlib */
2798 internal static unsafe void memset (byte *dest, int val, int len)
2809 val = val | (val << 8);
2810 val = val | (val << 16);
2813 int rest = (int)dest & 3;
2821 } while (rest != 0);
2824 ((int*)dest) [0] = val;
2825 ((int*)dest) [1] = val;
2826 ((int*)dest) [2] = val;
2827 ((int*)dest) [3] = val;
2832 ((int*)dest) [0] = val;
2844 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2845 /*while (size >= 32) {
2846 // using long is better than int and slower than double
2847 // FIXME: enable this only on correct alignment or on platforms
2848 // that can tolerate unaligned reads/writes of doubles
2849 ((double*)dest) [0] = ((double*)src) [0];
2850 ((double*)dest) [1] = ((double*)src) [1];
2851 ((double*)dest) [2] = ((double*)src) [2];
2852 ((double*)dest) [3] = ((double*)src) [3];
2857 while (size >= 16) {
2858 ((int*)dest) [0] = ((int*)src) [0];
2859 ((int*)dest) [1] = ((int*)src) [1];
2860 ((int*)dest) [2] = ((int*)src) [2];
2861 ((int*)dest) [3] = ((int*)src) [3];
2867 ((int*)dest) [0] = ((int*)src) [0];
2873 ((byte*)dest) [0] = ((byte*)src) [0];
2879 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2881 ((short*)dest) [0] = ((short*)src) [0];
2882 ((short*)dest) [1] = ((short*)src) [1];
2883 ((short*)dest) [2] = ((short*)src) [2];
2884 ((short*)dest) [3] = ((short*)src) [3];
2890 ((short*)dest) [0] = ((short*)src) [0];
2896 ((byte*)dest) [0] = ((byte*)src) [0];
2898 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2900 ((byte*)dest) [0] = ((byte*)src) [0];
2901 ((byte*)dest) [1] = ((byte*)src) [1];
2902 ((byte*)dest) [2] = ((byte*)src) [2];
2903 ((byte*)dest) [3] = ((byte*)src) [3];
2904 ((byte*)dest) [4] = ((byte*)src) [4];
2905 ((byte*)dest) [5] = ((byte*)src) [5];
2906 ((byte*)dest) [6] = ((byte*)src) [6];
2907 ((byte*)dest) [7] = ((byte*)src) [7];
2913 ((byte*)dest) [0] = ((byte*)src) [0];
2914 ((byte*)dest) [1] = ((byte*)src) [1];
2920 ((byte*)dest) [0] = ((byte*)src) [0];
2923 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2924 // FIXME: if pointers are not aligned, try to align them
2925 // so a faster routine can be used. Handle the case where
2926 // the pointers can't be reduced to have the same alignment
2927 // (just ignore the issue on x86?)
2928 if ((((int)dest | (int)src) & 3) != 0) {
2929 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2935 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2936 ((short*)dest) [0] = ((short*)src) [0];
2941 if ((((int)dest | (int)src) & 1) != 0) {
2942 memcpy1 (dest, src, size);
2945 if ((((int)dest | (int)src) & 2) != 0) {
2946 memcpy2 (dest, src, size);
2950 memcpy4 (dest, src, size);
2953 internal static unsafe void CharCopy (char *dest, char *src, int count) {
2954 // Same rules as for memcpy, but with the premise that
2955 // chars can only be aligned to even addresses if their
2956 // enclosing types are correctly aligned
2957 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
2958 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
2959 ((short*)dest) [0] = ((short*)src) [0];
2964 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
2965 memcpy2 ((byte*)dest, (byte*)src, count * 2);
2969 memcpy4 ((byte*)dest, (byte*)src, count * 2);
2972 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
2976 for (int i = count; i > 0; i--) {
2983 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
2985 fixed (char* dest = target, src = source)
2986 CharCopy (dest + targetIndex, src + sourceIndex, count);
2989 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
2991 fixed (char* dest = target, src = source)
2992 CharCopy (dest + targetIndex, src + sourceIndex, count);
2995 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
2996 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
2998 fixed (char* dest = target, src = source)
2999 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3002 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3003 unsafe public extern String (char *value);
3005 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3006 unsafe public extern String (char *value, int startIndex, int length);
3008 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3009 unsafe public extern String (sbyte *value);
3011 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3012 unsafe public extern String (sbyte *value, int startIndex, int length);
3014 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3015 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3017 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3018 public extern String (char [] value, int startIndex, int length);
3020 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3021 public extern String (char [] value);
3023 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3024 public extern String (char c, int count);
3026 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3027 private extern String[] InternalSplit (char[] separator, int count, int options);
3029 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3030 internal extern static String InternalAllocateStr (int length);
3032 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3033 private extern static string InternalIntern (string str);
3035 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3036 private extern static string InternalIsInterned (string str);
3038 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3039 private extern static int GetLOSLimit ();