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)
14 // Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
37 // This class contains all implementation for culture-insensitive methods.
38 // Culture-sensitive methods are implemented in the System.Globalization or
39 // Mono.Globalization namespace.
41 // Ensure that argument checks on methods don't overflow
45 using System.Collections;
46 using System.Globalization;
47 using System.Runtime.CompilerServices;
49 using System.Collections.Generic;
50 using System.Runtime.ConstrainedExecution;
51 using System.Runtime.InteropServices;
52 using Mono.Globalization.Unicode;
59 [StructLayout (LayoutKind.Sequential)]
60 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
62 [NonSerialized] private int length;
63 [NonSerialized] private char start_char;
65 public static readonly String Empty = "";
67 internal static readonly int LOS_limit = GetLOSLimit ();
69 internal static bool LegacyMode {
75 public static unsafe bool Equals (string a, string b)
77 if ((a as object) == (b as object))
80 if (a == null || b == null)
88 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
93 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
94 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
95 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
96 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
105 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
106 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
115 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
123 return len == 0 || *s1_ptr == *s2_ptr;
127 public static bool operator == (String a, String b)
129 return Equals (a, b);
132 public static bool operator != (String a, String b)
134 return !Equals (a, b);
137 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public override bool Equals (Object obj)
140 return Equals (this, obj as String);
143 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
144 public bool Equals (String value)
146 return Equals (this, value);
149 [IndexerName ("Chars")]
150 public unsafe char this [int index] {
152 if (index < 0 || index >= length)
153 throw new IndexOutOfRangeException ();
154 fixed (char* c = &start_char)
159 public Object Clone ()
164 public TypeCode GetTypeCode ()
166 return TypeCode.String;
169 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
171 if (destination == null)
172 throw new ArgumentNullException ("destination");
174 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
175 if (destinationIndex < 0)
176 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
178 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
179 if (sourceIndex > Length - count)
180 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
181 if (destinationIndex > destination.Length - count)
182 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
184 fixed (char* dest = destination, src = this)
185 CharCopy (dest + destinationIndex, src + sourceIndex, count);
188 public char[] ToCharArray ()
190 return ToCharArray (0, length);
193 public unsafe char[] ToCharArray (int startIndex, int length)
196 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
198 throw new ArgumentOutOfRangeException ("length", "< 0");
199 if (startIndex > this.length - length)
200 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
202 char[] tmp = new char [length];
203 fixed (char* dest = tmp, src = this)
204 CharCopy (dest, src + startIndex, length);
208 public String [] Split (params char [] separator)
210 return Split (separator, int.MaxValue, 0);
213 public String[] Split (char[] separator, int count)
215 return Split (separator, count, 0);
219 public String[] Split (char[] separator, StringSplitOptions options)
221 return Split (separator, Int32.MaxValue, options);
225 public String[] Split (char[] separator, int count, StringSplitOptions options)
228 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
229 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
230 throw new ArgumentException ("Illegal enum value: " + options + ".");
232 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
233 return EmptyArray<string>.Value;
237 EmptyArray<string>.Value :
238 new String[1] { this };
241 return SplitByCharacters (separator, count, options != 0);
245 public String[] Split (string[] separator, StringSplitOptions options)
247 return Split (separator, Int32.MaxValue, options);
251 public String[] Split (string[] separator, int count, StringSplitOptions options)
254 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
255 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
256 throw new ArgumentException ("Illegal enum value: " + options + ".");
260 EmptyArray<string>.Value :
261 new String[1] { this };
264 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
266 if (separator == null || separator.Length == 0)
267 return SplitByCharacters (null, count, removeEmpty);
269 if (Length == 0 && removeEmpty)
270 return EmptyArray<string>.Value;
272 List<String> arr = new List<String> ();
276 while (pos < this.Length) {
278 int matchPos = Int32.MaxValue;
280 // Find the first position where any of the separators matches
281 for (int i = 0; i < separator.Length; ++i) {
282 string sep = separator [i];
283 if (sep == null || sep.Length == 0)
286 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
287 if (match >= 0 && match < matchPos) {
293 if (matchIndex == -1)
296 if (!(matchPos == pos && removeEmpty)) {
297 if (arr.Count == count - 1)
299 arr.Add (this.Substring (pos, matchPos - pos));
302 pos = matchPos + separator [matchIndex].Length;
308 return new String [] { this };
310 // string contained only separators
311 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
312 return EmptyArray<string>.Value;
314 if (!(removeEmpty && pos == this.Length))
315 arr.Add (this.Substring (pos));
317 return arr.ToArray ();
320 // .NET 2.0 compatibility only
322 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
325 int[] split_points = null;
326 int total_points = 0;
329 if (sep == null || sep.Length == 0) {
330 fixed (char* src = this) {
335 if (char.IsWhiteSpace (*src_ptr++)) {
336 if (split_points == null) {
337 split_points = new int[8];
338 } else if (split_points.Length == total_points) {
339 Array.Resize (ref split_points, split_points.Length * 2);
342 split_points[total_points++] = Length - len;
343 if (total_points == count && !removeEmpty)
350 fixed (char* src = this) {
351 fixed (char* sep_src = sep) {
353 char* sep_ptr_end = sep_src + sep.Length;
356 char* sep_ptr = sep_src;
358 if (*sep_ptr++ == *src_ptr) {
359 if (split_points == null) {
360 split_points = new int[8];
361 } else if (split_points.Length == total_points) {
362 Array.Resize (ref split_points, split_points.Length * 2);
365 split_points[total_points++] = Length - len;
366 if (total_points == count && !removeEmpty)
371 } while (sep_ptr != sep_ptr_end);
380 if (total_points == 0)
381 return new string[] { this };
383 var res = new string[Math.Min (total_points, count) + 1];
387 for (; i < total_points; ++i) {
388 var start = split_points[i];
389 res[i] = SubstringUnchecked (prev_index, start - prev_index);
390 prev_index = start + 1;
393 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
397 for (; i < total_points; ++i) {
398 var start = split_points[i];
399 length = start - prev_index;
404 res[used++] = SubstringUnchecked (prev_index, length);
407 prev_index = start + 1;
410 length = Length - prev_index;
412 res[used++] = SubstringUnchecked (prev_index, length);
414 if (used != res.Length)
415 Array.Resize (ref res, used);
421 public String Substring (int startIndex)
425 if (startIndex < 0 || startIndex > this.length)
426 throw new ArgumentOutOfRangeException ("startIndex");
428 return SubstringUnchecked (startIndex, this.length - startIndex);
431 public String Substring (int startIndex, int length)
434 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
436 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
437 if (startIndex > this.length)
438 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
439 if (startIndex > this.length - length)
440 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
441 if (startIndex == 0 && length == this.length)
444 return SubstringUnchecked (startIndex, length);
447 // This method is used by StringBuilder.ToString() and is expected to
448 // always create a new string object (or return String.Empty).
449 internal unsafe String SubstringUnchecked (int startIndex, int length)
454 string tmp = InternalAllocateStr (length);
455 fixed (char* dest = tmp, src = this) {
456 CharCopy (dest, src + startIndex, length);
461 public String Trim ()
465 int start = FindNotWhiteSpace (0, length, 1);
470 int end = FindNotWhiteSpace (length - 1, start, -1);
472 int newLength = end - start + 1;
473 if (newLength == length)
476 return SubstringUnchecked (start, newLength);
479 public String Trim (params char[] trimChars)
481 if (trimChars == null || trimChars.Length == 0)
486 int start = FindNotInTable (0, length, 1, trimChars);
491 int end = FindNotInTable (length - 1, start, -1, trimChars);
493 int newLength = end - start + 1;
494 if (newLength == length)
497 return SubstringUnchecked (start, newLength);
500 public String TrimStart (params char[] trimChars)
505 if (trimChars == null || trimChars.Length == 0)
506 start = FindNotWhiteSpace (0, length, 1);
508 start = FindNotInTable (0, length, 1, trimChars);
513 return SubstringUnchecked (start, length - start);
516 public String TrimEnd (params char[] trimChars)
521 if (trimChars == null || trimChars.Length == 0)
522 end = FindNotWhiteSpace (length - 1, -1, -1);
524 end = FindNotInTable (length - 1, -1, -1, trimChars);
530 return SubstringUnchecked (0, end);
533 unsafe int FindNotWhiteSpace (int pos, int target, int change)
535 fixed (char* src = this) {
536 while (pos != target) {
537 if (!char.IsWhiteSpace (src[pos]))
546 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
548 fixed (char* tablePtr = table, thisPtr = this) {
549 while (pos != target) {
550 char c = thisPtr[pos];
552 while (x < table.Length) {
553 if (c == tablePtr[x])
557 if (x == table.Length)
565 public static int Compare (String strA, String strB)
567 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
570 public static int Compare (String strA, String strB, bool ignoreCase)
572 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
575 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
578 throw new ArgumentNullException ("culture");
580 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
583 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
585 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
588 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
590 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
593 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
596 throw new ArgumentNullException ("culture");
598 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
599 throw new ArgumentOutOfRangeException ();
611 else if (strB == null) {
615 CompareOptions compopts;
618 compopts = CompareOptions.IgnoreCase;
620 compopts = CompareOptions.None;
622 // Need to cap the requested length to the
623 // length of the string, because
624 // CompareInfo.Compare will insist that length
625 // <= (string.Length - offset)
630 if (length > (strA.Length - indexA)) {
631 len1 = strA.Length - indexA;
634 if (length > (strB.Length - indexB)) {
635 len2 = strB.Length - indexB;
638 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
639 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
642 public static int Compare (string strA, string strB, StringComparison comparisonType)
644 switch (comparisonType) {
645 case StringComparison.CurrentCulture:
646 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
647 case StringComparison.CurrentCultureIgnoreCase:
648 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
649 case StringComparison.InvariantCulture:
650 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
651 case StringComparison.InvariantCultureIgnoreCase:
652 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
653 case StringComparison.Ordinal:
654 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
655 case StringComparison.OrdinalIgnoreCase:
656 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
658 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
659 throw new ArgumentException (msg, "comparisonType");
663 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
665 switch (comparisonType) {
666 case StringComparison.CurrentCulture:
667 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
668 case StringComparison.CurrentCultureIgnoreCase:
669 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
670 case StringComparison.InvariantCulture:
671 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
672 case StringComparison.InvariantCultureIgnoreCase:
673 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
674 case StringComparison.Ordinal:
675 return CompareOrdinal (strA, indexA, strB, indexB, length);
676 case StringComparison.OrdinalIgnoreCase:
677 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
679 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
680 throw new ArgumentException (msg, "comparisonType");
684 public static bool Equals (string a, string b, StringComparison comparisonType)
686 return Compare (a, b, comparisonType) == 0;
689 public bool Equals (string value, StringComparison comparisonType)
691 return Compare (value, this, comparisonType) == 0;
694 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
697 throw new ArgumentNullException ("culture");
699 return culture.CompareInfo.Compare (strA, strB, options);
702 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
705 throw new ArgumentNullException ("culture");
710 if (length > (strA.Length - indexA))
711 len1 = strA.Length - indexA;
713 if (length > (strB.Length - indexB))
714 len2 = strB.Length - indexB;
716 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
719 public int CompareTo (Object value)
724 if (!(value is String))
725 throw new ArgumentException ();
727 return String.Compare (this, (String) value);
730 public int CompareTo (String strB)
735 return Compare (this, strB);
738 public static int CompareOrdinal (String strA, String strB)
740 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
743 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
745 if (strA != null && strB != null)
747 if (indexA > strA.Length || indexA < 0)
748 throw new ArgumentOutOfRangeException ("indexA");
749 if (indexB > strB.Length || indexB < 0)
750 throw new ArgumentOutOfRangeException ("indexB");
752 throw new ArgumentOutOfRangeException ("length");
755 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
758 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
760 if (strA != null && strB != null)
762 if (indexA > strA.Length || indexA < 0)
763 throw new ArgumentOutOfRangeException ("indexA");
764 if (indexB > strB.Length || indexB < 0)
765 throw new ArgumentOutOfRangeException ("indexB");
767 throw new ArgumentOutOfRangeException ("length");
770 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
773 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
776 return strB == null ? 0 : -1;
781 int lengthA = Math.Min (lenA, strA.Length - indexA);
782 int lengthB = Math.Min (lenB, strB.Length - indexB);
784 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
787 fixed (char* aptr = strA, bptr = strB) {
788 char* ap = aptr + indexA;
789 char* end = ap + Math.Min (lengthA, lengthB);
790 char* bp = bptr + indexB;
797 return lengthA - lengthB;
802 // Fastest method for internal case insensitive comparison
804 internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
806 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
809 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
811 // Same as above, but checks versus uppercase characters
813 return strB == null ? 0 : -1;
818 int lengthA = Math.Min (lenA, strA.Length - indexA);
819 int lengthB = Math.Min (lenB, strB.Length - indexB);
821 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
824 fixed (char* aptr = strA, bptr = strB) {
825 char* ap = aptr + indexA;
826 char* end = ap + Math.Min (lengthA, lengthB);
827 char* bp = bptr + indexB;
830 char c1 = Char.ToUpperInvariant (*ap);
831 char c2 = Char.ToUpperInvariant (*bp);
838 return lengthA - lengthB;
842 public bool EndsWith (String value)
845 throw new ArgumentNullException ("value");
847 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
850 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
853 throw new ArgumentNullException ("value");
855 culture = CultureInfo.CurrentCulture;
857 return culture.CompareInfo.IsSuffix (this, value,
858 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
861 // Following methods are culture-insensitive
862 public int IndexOfAny (char [] anyOf)
865 throw new ArgumentNullException ();
866 if (this.length == 0)
869 return IndexOfAnyUnchecked (anyOf, 0, this.length);
872 public int IndexOfAny (char [] anyOf, int startIndex)
875 throw new ArgumentNullException ();
876 if (startIndex < 0 || startIndex > this.length)
877 throw new ArgumentOutOfRangeException ();
879 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
882 public int IndexOfAny (char [] anyOf, int startIndex, int count)
885 throw new ArgumentNullException ();
886 if (startIndex < 0 || startIndex > this.length)
887 throw new ArgumentOutOfRangeException ();
888 if (count < 0 || startIndex > this.length - count)
889 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
891 return IndexOfAnyUnchecked (anyOf, startIndex, count);
894 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
896 if (anyOf.Length == 0)
899 if (anyOf.Length == 1)
900 return IndexOfUnchecked (anyOf[0], startIndex, count);
902 fixed (char* any = anyOf) {
906 char* end_any_ptr = any + anyOf.Length;
908 while (++any_ptr != end_any_ptr) {
909 if (*any_ptr > highest) {
914 if (*any_ptr < lowest)
918 fixed (char* start = &start_char) {
919 char* ptr = start + startIndex;
920 char* end_ptr = ptr + count;
922 while (ptr != end_ptr) {
923 if (*ptr > highest || *ptr < lowest) {
929 return (int)(ptr - start);
932 while (++any_ptr != end_any_ptr) {
933 if (*ptr == *any_ptr)
934 return (int)(ptr - start);
945 public int IndexOf (string value, StringComparison comparisonType)
947 return IndexOf (value, 0, this.Length, comparisonType);
950 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
952 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
955 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
957 switch (comparisonType) {
958 case StringComparison.CurrentCulture:
959 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
960 case StringComparison.CurrentCultureIgnoreCase:
961 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
962 case StringComparison.InvariantCulture:
963 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
964 case StringComparison.InvariantCultureIgnoreCase:
965 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
966 case StringComparison.Ordinal:
967 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
968 case StringComparison.OrdinalIgnoreCase:
969 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
971 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
972 throw new ArgumentException (msg, "comparisonType");
976 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
979 throw new ArgumentNullException ("value");
981 throw new ArgumentOutOfRangeException ("startIndex");
982 if (count < 0 || (this.length - startIndex) < count)
983 throw new ArgumentOutOfRangeException ("count");
985 if (options == CompareOptions.Ordinal)
986 return IndexOfOrdinalUnchecked (value, startIndex, count);
987 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
990 internal unsafe int IndexOfOrdinalUnchecked (string value)
992 return IndexOfOrdinalUnchecked (value, 0, length);
995 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
997 int valueLen = value.Length;
998 if (count < valueLen)
1001 if (valueLen <= 1) {
1003 return IndexOfUnchecked (value[0], startIndex, count);
1007 fixed (char* thisptr = this, valueptr = value) {
1008 char* ap = thisptr + startIndex;
1009 char* thisEnd = ap + count - valueLen + 1;
1010 while (ap != thisEnd) {
1011 if (*ap == *valueptr) {
1012 for (int i = 1; i < valueLen; i++) {
1013 if (ap[i] != valueptr[i])
1016 return (int)(ap - thisptr);
1025 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1027 int valueLen = value.Length;
1028 if (count < valueLen)
1034 fixed (char* thisptr = this, valueptr = value) {
1035 char* ap = thisptr + startIndex;
1036 char* thisEnd = ap + count - valueLen + 1;
1037 while (ap != thisEnd) {
1038 for (int i = 0; i < valueLen; i++) {
1039 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1042 return (int)(ap - thisptr);
1050 public int LastIndexOf (string value, StringComparison comparisonType)
1052 if (this.Length == 0)
1053 return value.Length == 0 ? 0 : -1;
1055 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1058 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1060 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1063 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1065 switch (comparisonType) {
1066 case StringComparison.CurrentCulture:
1067 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1068 case StringComparison.CurrentCultureIgnoreCase:
1069 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1070 case StringComparison.InvariantCulture:
1071 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1072 case StringComparison.InvariantCultureIgnoreCase:
1073 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1074 case StringComparison.Ordinal:
1075 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1076 case StringComparison.OrdinalIgnoreCase:
1077 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1079 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1080 throw new ArgumentException (msg, "comparisonType");
1084 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1087 throw new ArgumentNullException ("value");
1088 if (this.Length == 0)
1089 return value.Length == 0 ? 0 : -1;
1090 if (value.Length == 0)
1091 return Math.Min (this.Length - 1, startIndex);
1092 if (startIndex < 0 || startIndex > length)
1093 throw new ArgumentOutOfRangeException ("startIndex");
1094 if (count < 0 || (startIndex < count - 1))
1095 throw new ArgumentOutOfRangeException ("count");
1097 if (options == CompareOptions.Ordinal)
1098 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1099 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1102 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1104 int valueLen = value.Length;
1105 if (count < valueLen)
1108 if (valueLen <= 1) {
1110 return LastIndexOfUnchecked (value[0], startIndex, count);
1114 fixed (char* thisptr = this, valueptr = value) {
1115 char* ap = thisptr + startIndex - valueLen + 1;
1116 char* thisEnd = ap - count + valueLen - 1;
1117 while (ap != thisEnd) {
1118 if (*ap == *valueptr) {
1119 for (int i = 1; i < valueLen; i++) {
1120 if (ap[i] != valueptr[i])
1123 return (int)(ap - thisptr);
1132 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1134 int valueLen = value.Length;
1135 if (count < valueLen)
1141 fixed (char* thisptr = this, valueptr = value) {
1142 char* ap = thisptr + startIndex - valueLen + 1;
1143 char* thisEnd = ap - count + valueLen - 1;
1144 while (ap != thisEnd) {
1145 for (int i = 0; i < valueLen; i++) {
1146 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1149 return (int)(ap - thisptr);
1157 // Following methods are culture-insensitive
1158 public int IndexOf (char value)
1160 if (this.length == 0)
1163 return IndexOfUnchecked (value, 0, this.length);
1166 public int IndexOf (char value, int startIndex)
1169 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1170 if (startIndex > this.length)
1171 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1173 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1176 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1179 public int IndexOf (char value, int startIndex, int count)
1181 if (startIndex < 0 || startIndex > this.length)
1182 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1184 throw new ArgumentOutOfRangeException ("count", "< 0");
1185 if (startIndex > this.length - count)
1186 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1188 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1191 return IndexOfUnchecked (value, startIndex, count);
1194 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1196 // It helps JIT compiler to optimize comparison
1197 int value_32 = (int)value;
1199 fixed (char* start = &start_char) {
1200 char* ptr = start + startIndex;
1201 char* end_ptr = ptr + (count >> 3 << 3);
1203 while (ptr != end_ptr) {
1204 if (*ptr == value_32)
1205 return (int)(ptr - start);
1206 if (ptr[1] == value_32)
1207 return (int)(ptr - start + 1);
1208 if (ptr[2] == value_32)
1209 return (int)(ptr - start + 2);
1210 if (ptr[3] == value_32)
1211 return (int)(ptr - start + 3);
1212 if (ptr[4] == value_32)
1213 return (int)(ptr - start + 4);
1214 if (ptr[5] == value_32)
1215 return (int)(ptr - start + 5);
1216 if (ptr[6] == value_32)
1217 return (int)(ptr - start + 6);
1218 if (ptr[7] == value_32)
1219 return (int)(ptr - start + 7);
1224 end_ptr += count & 0x07;
1225 while (ptr != end_ptr) {
1226 if (*ptr == value_32)
1227 return (int)(ptr - start);
1235 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1239 int end = startIndex + count;
1240 char c = Char.ToUpperInvariant (value);
1241 fixed (char* s = &start_char) {
1242 for (int i = startIndex; i < end; i++)
1243 if (Char.ToUpperInvariant (s [i]) == c)
1249 // Following methods are culture-sensitive
1250 public int IndexOf (String value)
1253 throw new ArgumentNullException ("value");
1254 if (value.Length == 0)
1256 if (this.length == 0)
1258 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.None);
1261 public int IndexOf (String value, int startIndex)
1263 return IndexOf (value, startIndex, this.length - startIndex);
1266 public int IndexOf (String value, int startIndex, int count)
1269 throw new ArgumentNullException ("value");
1270 if (startIndex < 0 || startIndex > this.length)
1271 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1272 if (count < 0 || startIndex > this.length - count)
1273 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1275 if (value.length == 0)
1278 if (startIndex == 0 && this.length == 0)
1284 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1287 // Following methods are culture-insensitive
1288 public int LastIndexOfAny (char [] anyOf)
1291 throw new ArgumentNullException ();
1292 if (this.length == 0)
1295 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1298 public int LastIndexOfAny (char [] anyOf, int startIndex)
1301 throw new ArgumentNullException ();
1302 if (this.length == 0)
1305 if (startIndex < 0 || startIndex >= this.length)
1306 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1308 if (this.length == 0)
1311 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1314 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1317 throw new ArgumentNullException ();
1318 if (this.length == 0)
1321 if ((startIndex < 0) || (startIndex >= this.Length))
1322 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1323 if ((count < 0) || (count > this.Length))
1324 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1325 if (startIndex - count + 1 < 0)
1326 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1328 if (this.length == 0)
1331 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1334 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1336 if (anyOf.Length == 1)
1337 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1339 fixed (char* start = this, testStart = anyOf) {
1340 char* ptr = start + startIndex;
1341 char* ptrEnd = ptr - count;
1343 char* testEnd = testStart + anyOf.Length;
1345 while (ptr != ptrEnd) {
1347 while (test != testEnd) {
1349 return (int)(ptr - start);
1358 // Following methods are culture-insensitive
1359 public int LastIndexOf (char value)
1361 if (this.length == 0)
1364 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1367 public int LastIndexOf (char value, int startIndex)
1369 return LastIndexOf (value, startIndex, startIndex + 1);
1372 public int LastIndexOf (char value, int startIndex, int count)
1374 if (this.length == 0)
1377 // >= for char (> for string)
1378 if ((startIndex < 0) || (startIndex >= this.Length))
1379 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1380 if ((count < 0) || (count > this.Length))
1381 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1382 if (startIndex - count + 1 < 0)
1383 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1385 return LastIndexOfUnchecked (value, startIndex, count);
1388 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1390 // It helps JIT compiler to optimize comparison
1391 int value_32 = (int)value;
1393 fixed (char* start = &start_char) {
1394 char* ptr = start + startIndex;
1395 char* end_ptr = ptr - (count >> 3 << 3);
1397 while (ptr != end_ptr) {
1398 if (*ptr == value_32)
1399 return (int)(ptr - start);
1400 if (ptr[-1] == value_32)
1401 return (int)(ptr - start) - 1;
1402 if (ptr[-2] == value_32)
1403 return (int)(ptr - start) - 2;
1404 if (ptr[-3] == value_32)
1405 return (int)(ptr - start) - 3;
1406 if (ptr[-4] == value_32)
1407 return (int)(ptr - start) - 4;
1408 if (ptr[-5] == value_32)
1409 return (int)(ptr - start) - 5;
1410 if (ptr[-6] == value_32)
1411 return (int)(ptr - start) - 6;
1412 if (ptr[-7] == value_32)
1413 return (int)(ptr - start) - 7;
1418 end_ptr -= count & 0x07;
1419 while (ptr != end_ptr) {
1420 if (*ptr == value_32)
1421 return (int)(ptr - start);
1429 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1433 int end = startIndex - count;
1434 char c = Char.ToUpperInvariant (value);
1435 fixed (char* s = &start_char) {
1436 for (int i = startIndex; i > end; i--)
1437 if (Char.ToUpperInvariant (s [i]) == c)
1443 // Following methods are culture-sensitive
1444 public int LastIndexOf (String value)
1446 return LastIndexOf (value, this.length - 1, this.length);
1449 public int LastIndexOf (String value, int startIndex)
1451 int max = startIndex;
1452 if (max < this.Length)
1454 return LastIndexOf (value, startIndex, max);
1457 public int LastIndexOf (String value, int startIndex, int count)
1460 throw new ArgumentNullException ("value");
1462 if (this.length == 0)
1463 return value.Length == 0 ? 0 : -1;
1464 // -1 > startIndex > for string (0 > startIndex >= for char)
1465 if ((startIndex < -1) || (startIndex > this.Length))
1466 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1467 if ((count < 0) || (count > this.Length))
1468 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1469 if (startIndex - count + 1 < 0)
1470 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1472 if (value.Length == 0)
1473 return Math.Min (this.Length - 1, startIndex);
1475 if (startIndex == 0 && this.length == 0)
1478 // This check is needed to match undocumented MS behaviour
1479 if (this.length == 0 && value.length > 0)
1485 if (startIndex == this.Length)
1487 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1490 public bool Contains (String value)
1493 throw new ArgumentNullException ("value");
1495 return IndexOfOrdinalUnchecked (value, 0, Length) != -1;
1498 public static bool IsNullOrEmpty (String value)
1500 return (value == null) || (value.Length == 0);
1503 public string Normalize ()
1505 return Normalization.Normalize (this, 0);
1508 public string Normalize (NormalizationForm normalizationForm)
1510 switch (normalizationForm) {
1512 return Normalization.Normalize (this, 0);
1513 case NormalizationForm.FormD:
1514 return Normalization.Normalize (this, 1);
1515 case NormalizationForm.FormKC:
1516 return Normalization.Normalize (this, 2);
1517 case NormalizationForm.FormKD:
1518 return Normalization.Normalize (this, 3);
1522 public bool IsNormalized ()
1524 return Normalization.IsNormalized (this, 0);
1527 public bool IsNormalized (NormalizationForm normalizationForm)
1529 switch (normalizationForm) {
1531 return Normalization.IsNormalized (this, 0);
1532 case NormalizationForm.FormD:
1533 return Normalization.IsNormalized (this, 1);
1534 case NormalizationForm.FormKC:
1535 return Normalization.IsNormalized (this, 2);
1536 case NormalizationForm.FormKD:
1537 return Normalization.IsNormalized (this, 3);
1541 public string Remove (int startIndex)
1544 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1545 if (startIndex >= this.length)
1546 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1548 return Remove (startIndex, this.length - startIndex);
1551 public String PadLeft (int totalWidth)
1553 return PadLeft (totalWidth, ' ');
1556 public unsafe String PadLeft (int totalWidth, char paddingChar)
1558 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1561 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1563 if (totalWidth < this.length)
1565 if (totalWidth == 0)
1568 String tmp = InternalAllocateStr (totalWidth);
1570 fixed (char* dest = tmp, src = this) {
1571 char* padPos = dest;
1574 padTo = checked (dest + (totalWidth - length));
1575 } catch (OverflowException) {
1576 throw new OutOfMemoryException ();
1579 while (padPos != padTo)
1580 *padPos++ = paddingChar;
1582 CharCopy (padTo, src, length);
1587 public String PadRight (int totalWidth)
1589 return PadRight (totalWidth, ' ');
1592 public unsafe String PadRight (int totalWidth, char paddingChar)
1594 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1597 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1599 if (totalWidth < this.length)
1601 if (totalWidth == 0)
1604 String tmp = InternalAllocateStr (totalWidth);
1606 fixed (char* dest = tmp, src = this) {
1607 CharCopy (dest, src, length);
1610 char* padPos = checked (dest + length);
1611 char* padTo = checked (dest + totalWidth);
1612 while (padPos != padTo)
1613 *padPos++ = paddingChar;
1614 } catch (OverflowException) {
1615 throw new OutOfMemoryException ();
1621 public bool StartsWith (String value)
1624 throw new ArgumentNullException ("value");
1626 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1629 [ComVisible (false)]
1630 public bool StartsWith (string value, StringComparison comparisonType)
1633 throw new ArgumentNullException ("value");
1635 switch (comparisonType) {
1636 case StringComparison.CurrentCulture:
1637 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1638 case StringComparison.CurrentCultureIgnoreCase:
1639 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1640 case StringComparison.InvariantCulture:
1641 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1642 case StringComparison.InvariantCultureIgnoreCase:
1643 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1644 case StringComparison.Ordinal:
1645 return StartsWithOrdinalUnchecked (value);
1646 case StringComparison.OrdinalIgnoreCase:
1647 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1649 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1650 throw new ArgumentException (msg, "comparisonType");
1654 internal bool StartsWithOrdinalUnchecked (string value)
1656 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1659 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1661 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1664 [ComVisible (false)]
1665 public bool EndsWith (string value, StringComparison comparisonType)
1668 throw new ArgumentNullException ("value");
1670 switch (comparisonType) {
1671 case StringComparison.CurrentCulture:
1672 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1673 case StringComparison.CurrentCultureIgnoreCase:
1674 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1675 case StringComparison.InvariantCulture:
1676 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1677 case StringComparison.InvariantCultureIgnoreCase:
1678 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1679 case StringComparison.Ordinal:
1680 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1681 case StringComparison.OrdinalIgnoreCase:
1682 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1684 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1685 throw new ArgumentException (msg, "comparisonType");
1689 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1691 if (culture == null)
1692 culture = CultureInfo.CurrentCulture;
1694 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1697 // Following method is culture-insensitive
1698 public unsafe String Replace (char oldChar, char newChar)
1700 if (this.length == 0 || oldChar == newChar)
1703 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1704 if (start_pos == -1)
1710 string tmp = InternalAllocateStr (length);
1711 fixed (char* dest = tmp, src = &start_char) {
1713 CharCopy (dest, src, start_pos);
1715 char* end_ptr = dest + length;
1716 char* dest_ptr = dest + start_pos;
1717 char* src_ptr = src + start_pos;
1719 while (dest_ptr != end_ptr) {
1720 if (*src_ptr == oldChar)
1721 *dest_ptr = newChar;
1723 *dest_ptr = *src_ptr;
1732 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1733 public String Replace (String oldValue, String newValue)
1735 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1736 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1738 if (oldValue == null)
1739 throw new ArgumentNullException ("oldValue");
1741 if (oldValue.Length == 0)
1742 throw new ArgumentException ("oldValue is the empty string.");
1744 if (this.Length == 0)
1747 if (newValue == null)
1750 return ReplaceUnchecked (oldValue, newValue);
1753 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1755 if (oldValue.length > length)
1757 if (oldValue.length == 1 && newValue.length == 1) {
1758 return Replace (oldValue[0], newValue[0]);
1759 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1760 // because the length of the result would be this.length and length calculation unneccesary
1763 const int maxValue = 200; // Allocate 800 byte maximum
1764 int* dat = stackalloc int[maxValue];
1765 fixed (char* source = this, replace = newValue) {
1766 int i = 0, count = 0;
1767 while (i < length) {
1768 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1772 if (count < maxValue)
1773 dat[count++] = found;
1775 return ReplaceFallback (oldValue, newValue, maxValue);
1777 i = found + oldValue.length;
1784 nlen = this.length + ((newValue.length - oldValue.length) * count);
1785 } catch (OverflowException) {
1786 throw new OutOfMemoryException ();
1789 String tmp = InternalAllocateStr (nlen);
1791 int curPos = 0, lastReadPos = 0;
1792 fixed (char* dest = tmp) {
1793 for (int j = 0; j < count; j++) {
1794 int precopy = dat[j] - lastReadPos;
1795 CharCopy (dest + curPos, source + lastReadPos, precopy);
1797 lastReadPos = dat[j] + oldValue.length;
1798 CharCopy (dest + curPos, replace, newValue.length);
1799 curPos += newValue.length;
1801 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1807 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1809 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1810 StringBuilder sb = new StringBuilder (lengthEstimate);
1811 for (int i = 0; i < length;) {
1812 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1814 sb.Append (SubstringUnchecked (i, length - i));
1817 sb.Append (SubstringUnchecked (i, found - i));
1818 sb.Append (newValue);
1819 i = found + oldValue.Length;
1821 return sb.ToString ();
1825 public unsafe String Remove (int startIndex, int count)
1828 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1830 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1831 if (startIndex > this.length - count)
1832 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1834 String tmp = InternalAllocateStr (this.length - count);
1836 fixed (char *dest = tmp, src = this) {
1838 CharCopy (dst, src, startIndex);
1839 int skip = startIndex + count;
1841 CharCopy (dst, src + skip, length - skip);
1846 public String ToLower ()
1848 return ToLower (CultureInfo.CurrentCulture);
1851 public String ToLower (CultureInfo culture)
1853 if (culture == null)
1854 throw new ArgumentNullException ("culture");
1856 if (culture.LCID == 0x007F) // Invariant
1857 return ToLowerInvariant ();
1859 return culture.TextInfo.ToLower (this);
1862 public unsafe String ToLowerInvariant ()
1867 string tmp = InternalAllocateStr (length);
1868 fixed (char* source = &start_char, dest = tmp) {
1870 char* destPtr = (char*)dest;
1871 char* sourcePtr = (char*)source;
1873 for (int n = 0; n < length; n++) {
1874 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1882 public String ToUpper ()
1884 return ToUpper (CultureInfo.CurrentCulture);
1887 public String ToUpper (CultureInfo culture)
1889 if (culture == null)
1890 throw new ArgumentNullException ("culture");
1892 if (culture.LCID == 0x007F) // Invariant
1893 return ToUpperInvariant ();
1895 return culture.TextInfo.ToUpper (this);
1898 public unsafe String ToUpperInvariant ()
1903 string tmp = InternalAllocateStr (length);
1904 fixed (char* source = &start_char, dest = tmp) {
1906 char* destPtr = (char*)dest;
1907 char* sourcePtr = (char*)source;
1909 for (int n = 0; n < length; n++) {
1910 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1918 public override String ToString ()
1923 public String ToString (IFormatProvider provider)
1928 public static String Format (String format, Object arg0)
1930 return Format (null, format, new Object[] {arg0});
1933 public static String Format (String format, Object arg0, Object arg1)
1935 return Format (null, format, new Object[] {arg0, arg1});
1938 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1940 return Format (null, format, new Object[] {arg0, arg1, arg2});
1943 public static string Format (string format, params object[] args)
1945 return Format (null, format, args);
1948 public static string Format (IFormatProvider provider, string format, params object[] args)
1950 StringBuilder b = FormatHelper (null, provider, format, args);
1951 return b.ToString ();
1954 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1957 throw new ArgumentNullException ("format");
1959 throw new ArgumentNullException ("args");
1961 if (result == null) {
1962 /* Try to approximate the size of result to avoid reallocations */
1966 for (i = 0; i < args.Length; ++i) {
1967 string s = args [i] as string;
1973 if (i == args.Length)
1974 result = new StringBuilder (len + format.length);
1976 result = new StringBuilder ();
1981 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1983 while (ptr < format.length) {
1984 char c = format[ptr ++];
1987 result.Append (format, start, ptr - start - 1);
1989 // check for escaped open bracket
1991 if (format[ptr] == '{') {
2002 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2003 if (n >= args.Length)
2004 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2008 object arg = args[n];
2013 else if (formatter != null)
2014 str = formatter.Format (arg_format, arg, provider);
2019 if (arg is IFormattable)
2020 str = ((IFormattable)arg).ToString (arg_format, provider);
2022 str = arg.ToString () ?? Empty;
2025 // pad formatted string and append to result
2026 if (width > str.length) {
2027 const char padchar = ' ';
2028 int padlen = width - str.length;
2031 result.Append (str);
2032 result.Append (padchar, padlen);
2035 result.Append (padchar, padlen);
2036 result.Append (str);
2039 result.Append (str);
2044 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2045 result.Append (format, start, ptr - start - 1);
2048 else if (c == '}') {
2049 throw new FormatException ("Input string was not in a correct format.");
2053 if (start < format.length)
2054 result.Append (format, start, format.Length - start);
2059 public unsafe static String Copy (String str)
2062 throw new ArgumentNullException ("str");
2064 int length = str.length;
2066 String tmp = InternalAllocateStr (length);
2068 fixed (char *dest = tmp, src = str) {
2069 CharCopy (dest, src, length);
2075 public static String Concat (Object arg0)
2080 return arg0.ToString ();
2083 public static String Concat (Object arg0, Object arg1)
2085 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2088 public static String Concat (Object arg0, Object arg1, Object arg2)
2094 s1 = arg0.ToString ();
2099 s2 = arg1.ToString ();
2104 s3 = arg2.ToString ();
2106 return Concat (s1, s2, s3);
2109 [CLSCompliant(false)]
2110 public static String Concat (Object arg0, Object arg1, Object arg2,
2111 Object arg3, __arglist)
2113 string s1, s2, s3, s4;
2118 s1 = arg0.ToString ();
2123 s2 = arg1.ToString ();
2128 s3 = arg2.ToString ();
2130 ArgIterator iter = new ArgIterator (__arglist);
2131 int argCount = iter.GetRemainingCount();
2133 StringBuilder sb = new StringBuilder ();
2135 sb.Append (arg3.ToString ());
2137 for (int i = 0; i < argCount; i++) {
2138 TypedReference typedRef = iter.GetNextArg ();
2139 sb.Append (TypedReference.ToObject (typedRef));
2142 s4 = sb.ToString ();
2144 return Concat (s1, s2, s3, s4);
2147 public unsafe static String Concat (String str0, String str1)
2149 if (str0 == null || str0.Length == 0) {
2150 if (str1 == null || str1.Length == 0)
2155 if (str1 == null || str1.Length == 0)
2158 int nlen = str0.length + str1.length;
2160 throw new OutOfMemoryException ();
2161 String tmp = InternalAllocateStr (nlen);
2163 fixed (char *dest = tmp, src = str0)
2164 CharCopy (dest, src, str0.length);
2165 fixed (char *dest = tmp, src = str1)
2166 CharCopy (dest + str0.Length, src, str1.length);
2171 public unsafe static String Concat (String str0, String str1, String str2)
2173 if (str0 == null || str0.Length == 0){
2174 if (str1 == null || str1.Length == 0){
2175 if (str2 == null || str2.Length == 0)
2179 if (str2 == null || str2.Length == 0)
2184 if (str1 == null || str1.Length == 0){
2185 if (str2 == null || str2.Length == 0)
2190 if (str2 == null || str2.Length == 0)
2195 int nlen = str0.length + str1.length;
2197 throw new OutOfMemoryException ();
2198 nlen += str2.length;
2200 throw new OutOfMemoryException ();
2201 String tmp = InternalAllocateStr (nlen);
2203 if (str0.Length != 0) {
2204 fixed (char *dest = tmp, src = str0) {
2205 CharCopy (dest, src, str0.length);
2208 if (str1.Length != 0) {
2209 fixed (char *dest = tmp, src = str1) {
2210 CharCopy (dest + str0.Length, src, str1.length);
2213 if (str2.Length != 0) {
2214 fixed (char *dest = tmp, src = str2) {
2215 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2222 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2224 if (str0 == null && str1 == null && str2 == null && str3 == null)
2236 int nlen = str0.length + str1.length;
2238 throw new OutOfMemoryException ();
2239 nlen += str2.length;
2241 throw new OutOfMemoryException ();
2242 nlen += str3.length;
2244 throw new OutOfMemoryException ();
2245 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2247 if (str0.Length != 0) {
2248 fixed (char *dest = tmp, src = str0) {
2249 CharCopy (dest, src, str0.length);
2252 if (str1.Length != 0) {
2253 fixed (char *dest = tmp, src = str1) {
2254 CharCopy (dest + str0.Length, src, str1.length);
2257 if (str2.Length != 0) {
2258 fixed (char *dest = tmp, src = str2) {
2259 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2262 if (str3.Length != 0) {
2263 fixed (char *dest = tmp, src = str3) {
2264 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2271 public static String Concat (params Object[] args)
2274 throw new ArgumentNullException ("args");
2276 int argLen = args.Length;
2280 string [] strings = new string [argLen];
2282 for (int i = 0; i < argLen; i++) {
2283 if (args[i] != null) {
2284 strings[i] = args[i].ToString ();
2285 len += strings[i].length;
2287 throw new OutOfMemoryException ();
2291 return ConcatInternal (strings, len);
2294 public static String Concat (params String[] values)
2297 throw new ArgumentNullException ("values");
2300 for (int i = 0; i < values.Length; i++) {
2301 String s = values[i];
2305 throw new OutOfMemoryException ();
2308 return ConcatInternal (values, len);
2311 private static unsafe String ConcatInternal (String[] values, int length)
2316 throw new OutOfMemoryException ();
2318 String tmp = InternalAllocateStr (length);
2320 fixed (char* dest = tmp) {
2322 for (int i = 0; i < values.Length; i++) {
2323 String source = values[i];
2324 if (source != null) {
2325 fixed (char* src = source) {
2326 CharCopy (dest + pos, src, source.length);
2328 pos += source.Length;
2335 public unsafe String Insert (int startIndex, String value)
2338 throw new ArgumentNullException ("value");
2340 if (startIndex < 0 || startIndex > this.length)
2341 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2343 if (value.Length == 0)
2345 if (this.Length == 0)
2348 int nlen = this.length + value.length;
2350 throw new OutOfMemoryException ();
2352 String tmp = InternalAllocateStr (nlen);
2354 fixed (char *dest = tmp, src = this, val = value) {
2356 CharCopy (dst, src, startIndex);
2358 CharCopy (dst, val, value.length);
2359 dst += value.length;
2360 CharCopy (dst, src + startIndex, length - startIndex);
2365 public static string Intern (string str)
2368 throw new ArgumentNullException ("str");
2370 return InternalIntern (str);
2373 public static string IsInterned (string str)
2376 throw new ArgumentNullException ("str");
2378 return InternalIsInterned (str);
2381 public static string Join (string separator, params string [] value)
2384 throw new ArgumentNullException ("value");
2385 if (separator == null)
2388 return JoinUnchecked (separator, value, 0, value.Length);
2391 public static string Join (string separator, string[] value, int startIndex, int count)
2394 throw new ArgumentNullException ("value");
2396 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2398 throw new ArgumentOutOfRangeException ("count", "< 0");
2399 if (startIndex > value.Length - count)
2400 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2402 if (startIndex == value.Length)
2404 if (separator == null)
2407 return JoinUnchecked (separator, value, startIndex, count);
2410 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2412 // Unchecked parameters
2413 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2414 // separator and value must not be null
2417 int maxIndex = startIndex + count;
2418 // Precount the number of characters that the resulting string will have
2419 for (int i = startIndex; i < maxIndex; i++) {
2420 String s = value[i];
2424 length += separator.length * (count - 1);
2428 String tmp = InternalAllocateStr (length);
2431 fixed (char* dest = tmp, sepsrc = separator) {
2432 // Copy each string from value except the last one and add a separator for each
2434 for (int i = startIndex; i < maxIndex; i++) {
2435 String source = value[i];
2436 if (source != null) {
2437 if (source.Length > 0) {
2438 fixed (char* src = source)
2439 CharCopy (dest + pos, src, source.Length);
2440 pos += source.Length;
2443 if (separator.Length > 0) {
2444 CharCopy (dest + pos, sepsrc, separator.Length);
2445 pos += separator.Length;
2448 // Append last string that does not get an additional separator
2449 String sourceLast = value[maxIndex];
2450 if (sourceLast != null) {
2451 if (sourceLast.Length > 0) {
2452 fixed (char* src = sourceLast)
2453 CharCopy (dest + pos, src, sourceLast.Length);
2460 bool IConvertible.ToBoolean (IFormatProvider provider)
2462 return Convert.ToBoolean (this, provider);
2465 byte IConvertible.ToByte (IFormatProvider provider)
2467 return Convert.ToByte (this, provider);
2470 char IConvertible.ToChar (IFormatProvider provider)
2472 return Convert.ToChar (this, provider);
2475 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2477 return Convert.ToDateTime (this, provider);
2480 decimal IConvertible.ToDecimal (IFormatProvider provider)
2482 return Convert.ToDecimal (this, provider);
2485 double IConvertible.ToDouble (IFormatProvider provider)
2487 return Convert.ToDouble (this, provider);
2490 short IConvertible.ToInt16 (IFormatProvider provider)
2492 return Convert.ToInt16 (this, provider);
2495 int IConvertible.ToInt32 (IFormatProvider provider)
2497 return Convert.ToInt32 (this, provider);
2500 long IConvertible.ToInt64 (IFormatProvider provider)
2502 return Convert.ToInt64 (this, provider);
2505 sbyte IConvertible.ToSByte (IFormatProvider provider)
2507 return Convert.ToSByte (this, provider);
2510 float IConvertible.ToSingle (IFormatProvider provider)
2512 return Convert.ToSingle (this, provider);
2515 object IConvertible.ToType (Type type, IFormatProvider provider)
2517 return Convert.DefaultToType ((IConvertible)this, type, provider);
2520 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2522 return Convert.ToUInt16 (this, provider);
2525 uint IConvertible.ToUInt32 (IFormatProvider provider)
2527 return Convert.ToUInt32 (this, provider);
2530 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2532 return Convert.ToUInt64 (this, provider);
2541 public CharEnumerator GetEnumerator ()
2543 return new CharEnumerator (this);
2546 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2548 return new CharEnumerator (this);
2551 IEnumerator IEnumerable.GetEnumerator ()
2553 return new CharEnumerator (this);
2556 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2557 out bool left_align, out string format)
2559 int max = str.Length;
2561 // parses format specifier of form:
2565 // N = argument number (non-negative integer)
2567 n = ParseDecimal (str, ref ptr);
2569 throw new FormatException ("Input string was not in a correct format.");
2571 // M = width (non-negative integer)
2573 if (ptr < max && str[ptr] == ',') {
2574 // White space between ',' and number or sign.
2576 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2580 format = str.Substring (start, ptr - start);
2582 left_align = (ptr < max && str [ptr] == '-');
2586 width = ParseDecimal (str, ref ptr);
2588 throw new FormatException ("Input string was not in a correct format.");
2596 // F = argument format (string)
2598 if (ptr < max && str[ptr] == ':') {
2601 if (str [ptr] == '}') {
2602 if (ptr + 1 < max && str [ptr + 1] == '}') {
2604 format += str.Substring (start, ptr - start);
2616 format += str.Substring (start, ptr - start);
2621 if ((ptr >= max) || str[ptr ++] != '}')
2622 throw new FormatException ("Input string was not in a correct format.");
2625 private static int ParseDecimal (string str, ref int ptr)
2629 int max = str.Length;
2633 if (c < '0' || '9' < c)
2636 n = n * 10 + c - '0';
2640 if (p == ptr || p == max)
2647 internal unsafe void InternalSetChar (int idx, char val)
2649 if ((uint) idx >= (uint) Length)
2650 throw new ArgumentOutOfRangeException ("idx");
2652 fixed (char * pStr = &start_char)
2658 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2659 internal extern void InternalSetLength (int newLength);
2661 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2662 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2663 public unsafe override int GetHashCode ()
2665 fixed (char * c = this) {
2667 char * end = cc + length - 1;
2669 for (;cc < end; cc += 2) {
2670 h = (h << 5) - h + *cc;
2671 h = (h << 5) - h + cc [1];
2675 h = (h << 5) - h + *cc;
2681 public static string Concat (IEnumerable<string> values)
2684 throw new ArgumentNullException ("values");
2686 var stringList = new List<string> ();
2688 foreach (var v in values){
2693 throw new OutOfMemoryException ();
2696 return ConcatInternal (stringList.ToArray (), len);
2699 [ComVisibleAttribute(false)]
2700 public static string Concat<T> (IEnumerable<T> values)
2703 throw new ArgumentNullException ("values");
2705 var stringList = new List<string> ();
2707 foreach (var v in values){
2708 string sr = v.ToString ();
2711 throw new OutOfMemoryException ();
2712 stringList.Add (sr);
2714 return ConcatInternal (stringList.ToArray (), len);
2717 [ComVisibleAttribute(false)]
2718 public static string Join (string separator, IEnumerable<string> values)
2720 if (separator == null)
2721 return Concat (values);
2724 throw new ArgumentNullException ("values");
2726 var stringList = new List<string> (values);
2728 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2731 [ComVisibleAttribute(false)]
2732 public static string Join (string separator, params object [] values)
2734 if (separator == null)
2735 return Concat (values);
2738 throw new ArgumentNullException ("values");
2740 var strCopy = new string [values.Length];
2742 foreach (var v in values)
2743 strCopy [i++] = v.ToString ();
2745 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2748 [ComVisible (false)]
2749 public static string Join<T> (string separator, IEnumerable<T> values)
2751 if (separator == null)
2752 return Concat<T> (values);
2755 throw new ArgumentNullException ("values");
2757 var stringList = values as IList<T> ?? new List<T> (values);
2758 var strCopy = new string [stringList.Count];
2760 foreach (var v in stringList)
2761 strCopy [i++] = v.ToString ();
2763 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2766 public static bool IsNullOrWhiteSpace (string value)
2768 if ((value == null) || (value.Length == 0))
2770 foreach (char c in value)
2771 if (!Char.IsWhiteSpace (c))
2776 internal unsafe int GetCaseInsensitiveHashCode ()
2778 fixed (char * c = this) {
2780 char * end = cc + length - 1;
2782 for (;cc < end; cc += 2) {
2783 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2784 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2788 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2793 // Certain constructors are redirected to CreateString methods with
2794 // matching argument list. The this pointer should not be used.
2796 private unsafe String CreateString (sbyte* value)
2801 byte* bytes = (byte*) value;
2805 while (bytes++ [0] != 0)
2807 } catch (NullReferenceException) {
2808 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2811 return CreateString (value, 0, length, null);
2814 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2816 return CreateString (value, startIndex, length, null);
2819 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2822 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2824 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2825 if (value + startIndex < value)
2826 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2830 throw new ArgumentNullException ("value");
2834 enc = Encoding.Default;
2837 byte [] bytes = new byte [length];
2840 fixed (byte* bytePtr = bytes)
2843 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2844 memcpy (bytePtr, (byte*) (value + startIndex), length);
2845 } catch (NullReferenceException) {
2846 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2849 // GetString () is called even when length == 0
2850 return enc.GetString (bytes);
2853 unsafe string CreateString (char *value)
2863 string result = InternalAllocateStr (i);
2866 fixed (char *dest = result) {
2867 CharCopy (dest, value, i);
2873 unsafe string CreateString (char *value, int startIndex, int length)
2878 throw new ArgumentNullException ("value");
2880 throw new ArgumentOutOfRangeException ("startIndex");
2882 throw new ArgumentOutOfRangeException ("length");
2884 string result = InternalAllocateStr (length);
2886 fixed (char *dest = result) {
2887 CharCopy (dest, value + startIndex, length);
2892 unsafe string CreateString (char [] val, int startIndex, int length)
2895 throw new ArgumentNullException ("value");
2897 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2899 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2900 if (startIndex > val.Length - length)
2901 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2905 string result = InternalAllocateStr (length);
2907 fixed (char *dest = result, src = val) {
2908 CharCopy (dest, src + startIndex, length);
2913 unsafe string CreateString (char [] val)
2915 if (val == null || val.Length == 0)
2917 string result = InternalAllocateStr (val.Length);
2919 fixed (char *dest = result, src = val) {
2920 CharCopy (dest, src, val.Length);
2925 unsafe string CreateString (char c, int count)
2928 throw new ArgumentOutOfRangeException ("count");
2931 string result = InternalAllocateStr (count);
2932 fixed (char *dest = result) {
2934 char *end = p + count;
2943 /* helpers used by the runtime as well as above or eslewhere in corlib */
2944 internal static unsafe void memset (byte *dest, int val, int len)
2955 val = val | (val << 8);
2956 val = val | (val << 16);
2959 int rest = (int)dest & 3;
2967 } while (rest != 0);
2970 ((int*)dest) [0] = val;
2971 ((int*)dest) [1] = val;
2972 ((int*)dest) [2] = val;
2973 ((int*)dest) [3] = val;
2978 ((int*)dest) [0] = val;
2990 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2991 /*while (size >= 32) {
2992 // using long is better than int and slower than double
2993 // FIXME: enable this only on correct alignment or on platforms
2994 // that can tolerate unaligned reads/writes of doubles
2995 ((double*)dest) [0] = ((double*)src) [0];
2996 ((double*)dest) [1] = ((double*)src) [1];
2997 ((double*)dest) [2] = ((double*)src) [2];
2998 ((double*)dest) [3] = ((double*)src) [3];
3003 while (size >= 16) {
3004 ((int*)dest) [0] = ((int*)src) [0];
3005 ((int*)dest) [1] = ((int*)src) [1];
3006 ((int*)dest) [2] = ((int*)src) [2];
3007 ((int*)dest) [3] = ((int*)src) [3];
3013 ((int*)dest) [0] = ((int*)src) [0];
3019 ((byte*)dest) [0] = ((byte*)src) [0];
3025 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3027 ((short*)dest) [0] = ((short*)src) [0];
3028 ((short*)dest) [1] = ((short*)src) [1];
3029 ((short*)dest) [2] = ((short*)src) [2];
3030 ((short*)dest) [3] = ((short*)src) [3];
3036 ((short*)dest) [0] = ((short*)src) [0];
3042 ((byte*)dest) [0] = ((byte*)src) [0];
3044 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3046 ((byte*)dest) [0] = ((byte*)src) [0];
3047 ((byte*)dest) [1] = ((byte*)src) [1];
3048 ((byte*)dest) [2] = ((byte*)src) [2];
3049 ((byte*)dest) [3] = ((byte*)src) [3];
3050 ((byte*)dest) [4] = ((byte*)src) [4];
3051 ((byte*)dest) [5] = ((byte*)src) [5];
3052 ((byte*)dest) [6] = ((byte*)src) [6];
3053 ((byte*)dest) [7] = ((byte*)src) [7];
3059 ((byte*)dest) [0] = ((byte*)src) [0];
3060 ((byte*)dest) [1] = ((byte*)src) [1];
3066 ((byte*)dest) [0] = ((byte*)src) [0];
3069 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3070 // FIXME: if pointers are not aligned, try to align them
3071 // so a faster routine can be used. Handle the case where
3072 // the pointers can't be reduced to have the same alignment
3073 // (just ignore the issue on x86?)
3074 if ((((int)dest | (int)src) & 3) != 0) {
3075 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3081 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3082 ((short*)dest) [0] = ((short*)src) [0];
3087 if ((((int)dest | (int)src) & 1) != 0) {
3088 memcpy1 (dest, src, size);
3091 if ((((int)dest | (int)src) & 2) != 0) {
3092 memcpy2 (dest, src, size);
3096 memcpy4 (dest, src, size);
3099 /* Used by the runtime */
3100 internal static unsafe void bzero (byte *dest, int len) {
3101 memset (dest, 0, len);
3104 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3105 ((byte*)dest) [0] = 0;
3108 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3109 ((short*)dest) [0] = 0;
3112 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3113 ((int*)dest) [0] = 0;
3116 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3117 ((long*)dest) [0] = 0;
3120 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3121 ((byte*)dest) [0] = ((byte*)src) [0];
3124 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3125 ((short*)dest) [0] = ((short*)src) [0];
3128 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3129 ((int*)dest) [0] = ((int*)src) [0];
3132 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3133 ((long*)dest) [0] = ((long*)src) [0];
3136 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3137 // Same rules as for memcpy, but with the premise that
3138 // chars can only be aligned to even addresses if their
3139 // enclosing types are correctly aligned
3140 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3141 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3142 ((short*)dest) [0] = ((short*)src) [0];
3147 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3148 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3152 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3155 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3159 for (int i = count; i > 0; i--) {
3166 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3168 fixed (char* dest = target, src = source)
3169 CharCopy (dest + targetIndex, src + sourceIndex, count);
3172 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3174 fixed (char* dest = target, src = source)
3175 CharCopy (dest + targetIndex, src + sourceIndex, count);
3178 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3179 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3181 fixed (char* dest = target, src = source)
3182 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3185 internal static String FastAllocateString (int length)
3187 return InternalAllocateStr (length);
3190 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3191 unsafe public extern String (char *value);
3193 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3194 unsafe public extern String (char *value, int startIndex, int length);
3196 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3197 unsafe public extern String (sbyte *value);
3199 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3200 unsafe public extern String (sbyte *value, int startIndex, int length);
3202 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3203 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3205 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3206 public extern String (char [] value, int startIndex, int length);
3208 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3209 public extern String (char [] value);
3211 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3212 public extern String (char c, int count);
3214 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3215 internal extern static String InternalAllocateStr (int length);
3217 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3218 private extern static string InternalIntern (string str);
3220 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3221 private extern static string InternalIsInterned (string str);
3223 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3224 private extern static int GetLOSLimit ();