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;
54 using System.Diagnostics.Contracts;
60 [StructLayout (LayoutKind.Sequential)]
61 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
63 [NonSerialized] private int length;
64 [NonSerialized] private char start_char;
66 public static readonly String Empty = "";
68 internal static readonly int LOS_limit = GetLOSLimit ();
70 internal static bool LegacyMode {
76 public static unsafe bool Equals (string a, string b)
78 if ((a as object) == (b as object))
81 if (a == null || b == null)
89 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
94 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
95 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
96 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
97 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
106 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
107 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
116 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
124 return len == 0 || *s1_ptr == *s2_ptr;
128 public static bool operator == (String a, String b)
130 return Equals (a, b);
133 public static bool operator != (String a, String b)
135 return !Equals (a, b);
138 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
139 public override bool Equals (Object obj)
141 return Equals (this, obj as String);
144 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
145 public bool Equals (String value)
147 return Equals (this, value);
150 [IndexerName ("Chars")]
151 public unsafe char this [int index] {
153 if (index < 0 || index >= length)
154 throw new IndexOutOfRangeException ();
155 fixed (char* c = &start_char)
160 public Object Clone ()
165 public TypeCode GetTypeCode ()
167 return TypeCode.String;
170 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
172 if (destination == null)
173 throw new ArgumentNullException ("destination");
175 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
176 if (destinationIndex < 0)
177 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
179 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
180 if (sourceIndex > Length - count)
181 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
182 if (destinationIndex > destination.Length - count)
183 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
185 fixed (char* dest = destination, src = this)
186 CharCopy (dest + destinationIndex, src + sourceIndex, count);
189 public char[] ToCharArray ()
191 return ToCharArray (0, length);
194 public unsafe char[] ToCharArray (int startIndex, int length)
197 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
199 throw new ArgumentOutOfRangeException ("length", "< 0");
200 if (startIndex > this.length - length)
201 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
203 char[] tmp = new char [length];
204 fixed (char* dest = tmp, src = this)
205 CharCopy (dest, src + startIndex, length);
209 public String [] Split (params char [] separator)
211 return Split (separator, int.MaxValue, 0);
214 public String[] Split (char[] separator, int count)
216 return Split (separator, count, 0);
220 public String[] Split (char[] separator, StringSplitOptions options)
222 return Split (separator, Int32.MaxValue, options);
226 public String[] Split (char[] separator, int count, StringSplitOptions options)
229 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
230 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
231 throw new ArgumentException ("Illegal enum value: " + options + ".");
233 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
234 return EmptyArray<string>.Value;
238 EmptyArray<string>.Value :
239 new String[1] { this };
242 return SplitByCharacters (separator, count, options != 0);
246 public String[] Split (string[] separator, StringSplitOptions options)
248 return Split (separator, Int32.MaxValue, options);
252 public String[] Split (string[] separator, int count, StringSplitOptions options)
255 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
256 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
257 throw new ArgumentException ("Illegal enum value: " + options + ".");
261 EmptyArray<string>.Value :
262 new String[1] { this };
265 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
267 if (separator == null || separator.Length == 0)
268 return SplitByCharacters (null, count, removeEmpty);
270 if (Length == 0 && removeEmpty)
271 return EmptyArray<string>.Value;
273 List<String> arr = new List<String> ();
277 while (pos < this.Length) {
279 int matchPos = Int32.MaxValue;
281 // Find the first position where any of the separators matches
282 for (int i = 0; i < separator.Length; ++i) {
283 string sep = separator [i];
284 if (sep == null || sep.Length == 0)
287 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
288 if (match >= 0 && match < matchPos) {
294 if (matchIndex == -1)
297 if (!(matchPos == pos && removeEmpty)) {
298 if (arr.Count == count - 1)
300 arr.Add (this.Substring (pos, matchPos - pos));
303 pos = matchPos + separator [matchIndex].Length;
309 return new String [] { this };
311 // string contained only separators
312 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
313 return EmptyArray<string>.Value;
315 if (!(removeEmpty && pos == this.Length))
316 arr.Add (this.Substring (pos));
318 return arr.ToArray ();
321 // .NET 2.0 compatibility only
323 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
326 int[] split_points = null;
327 int total_points = 0;
330 if (sep == null || sep.Length == 0) {
331 fixed (char* src = this) {
336 if (char.IsWhiteSpace (*src_ptr++)) {
337 if (split_points == null) {
338 split_points = new int[8];
339 } else if (split_points.Length == total_points) {
340 Array.Resize (ref split_points, split_points.Length * 2);
343 split_points[total_points++] = Length - len;
344 if (total_points == count && !removeEmpty)
351 fixed (char* src = this) {
352 fixed (char* sep_src = sep) {
354 char* sep_ptr_end = sep_src + sep.Length;
357 char* sep_ptr = sep_src;
359 if (*sep_ptr++ == *src_ptr) {
360 if (split_points == null) {
361 split_points = new int[8];
362 } else if (split_points.Length == total_points) {
363 Array.Resize (ref split_points, split_points.Length * 2);
366 split_points[total_points++] = Length - len;
367 if (total_points == count && !removeEmpty)
372 } while (sep_ptr != sep_ptr_end);
381 if (total_points == 0)
382 return new string[] { this };
384 var res = new string[Math.Min (total_points, count) + 1];
388 for (; i < total_points; ++i) {
389 var start = split_points[i];
390 res[i] = SubstringUnchecked (prev_index, start - prev_index);
391 prev_index = start + 1;
394 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
398 for (; i < total_points; ++i) {
399 var start = split_points[i];
400 length = start - prev_index;
405 res[used++] = SubstringUnchecked (prev_index, length);
408 prev_index = start + 1;
411 length = Length - prev_index;
413 res[used++] = SubstringUnchecked (prev_index, length);
415 if (used != res.Length)
416 Array.Resize (ref res, used);
422 public String Substring (int startIndex)
426 if (startIndex < 0 || startIndex > this.length)
427 throw new ArgumentOutOfRangeException ("startIndex");
429 return SubstringUnchecked (startIndex, this.length - startIndex);
432 public String Substring (int startIndex, int length)
435 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
437 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
438 if (startIndex > this.length)
439 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
440 if (startIndex > this.length - length)
441 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
442 if (startIndex == 0 && length == this.length)
445 return SubstringUnchecked (startIndex, length);
448 // This method is used by StringBuilder.ToString() and is expected to
449 // always create a new string object (or return String.Empty).
450 internal unsafe String SubstringUnchecked (int startIndex, int length)
455 string tmp = InternalAllocateStr (length);
456 fixed (char* dest = tmp, src = this) {
457 CharCopy (dest, src + startIndex, length);
462 public String Trim ()
466 int start = FindNotWhiteSpace (0, length, 1);
471 int end = FindNotWhiteSpace (length - 1, start, -1);
473 int newLength = end - start + 1;
474 if (newLength == length)
477 return SubstringUnchecked (start, newLength);
480 public String Trim (params char[] trimChars)
482 if (trimChars == null || trimChars.Length == 0)
487 int start = FindNotInTable (0, length, 1, trimChars);
492 int end = FindNotInTable (length - 1, start, -1, trimChars);
494 int newLength = end - start + 1;
495 if (newLength == length)
498 return SubstringUnchecked (start, newLength);
501 public String TrimStart (params char[] trimChars)
506 if (trimChars == null || trimChars.Length == 0)
507 start = FindNotWhiteSpace (0, length, 1);
509 start = FindNotInTable (0, length, 1, trimChars);
514 return SubstringUnchecked (start, length - start);
517 public String TrimEnd (params char[] trimChars)
522 if (trimChars == null || trimChars.Length == 0)
523 end = FindNotWhiteSpace (length - 1, -1, -1);
525 end = FindNotInTable (length - 1, -1, -1, trimChars);
531 return SubstringUnchecked (0, end);
534 unsafe int FindNotWhiteSpace (int pos, int target, int change)
536 fixed (char* src = this) {
537 while (pos != target) {
538 if (!char.IsWhiteSpace (src[pos]))
547 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
549 fixed (char* tablePtr = table, thisPtr = this) {
550 while (pos != target) {
551 char c = thisPtr[pos];
553 while (x < table.Length) {
554 if (c == tablePtr[x])
558 if (x == table.Length)
566 public static int Compare (String strA, String strB)
568 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
571 public static int Compare (String strA, String strB, bool ignoreCase)
573 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
576 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
579 throw new ArgumentNullException ("culture");
581 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
584 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
586 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
589 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
591 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
594 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
597 throw new ArgumentNullException ("culture");
599 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
600 throw new ArgumentOutOfRangeException ();
612 else if (strB == null) {
616 CompareOptions compopts;
619 compopts = CompareOptions.IgnoreCase;
621 compopts = CompareOptions.None;
623 // Need to cap the requested length to the
624 // length of the string, because
625 // CompareInfo.Compare will insist that length
626 // <= (string.Length - offset)
631 if (length > (strA.Length - indexA)) {
632 len1 = strA.Length - indexA;
635 if (length > (strB.Length - indexB)) {
636 len2 = strB.Length - indexB;
639 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
640 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
643 public static int Compare (string strA, string strB, StringComparison comparisonType)
645 switch (comparisonType) {
646 case StringComparison.CurrentCulture:
647 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
648 case StringComparison.CurrentCultureIgnoreCase:
649 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
650 case StringComparison.InvariantCulture:
651 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
652 case StringComparison.InvariantCultureIgnoreCase:
653 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
654 case StringComparison.Ordinal:
655 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
656 case StringComparison.OrdinalIgnoreCase:
657 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
659 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
660 throw new ArgumentException (msg, "comparisonType");
664 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
666 switch (comparisonType) {
667 case StringComparison.CurrentCulture:
668 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
669 case StringComparison.CurrentCultureIgnoreCase:
670 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
671 case StringComparison.InvariantCulture:
672 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
673 case StringComparison.InvariantCultureIgnoreCase:
674 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
675 case StringComparison.Ordinal:
676 return CompareOrdinal (strA, indexA, strB, indexB, length);
677 case StringComparison.OrdinalIgnoreCase:
678 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
680 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
681 throw new ArgumentException (msg, "comparisonType");
685 public static bool Equals (string a, string b, StringComparison comparisonType)
687 return Compare (a, b, comparisonType) == 0;
690 public bool Equals (string value, StringComparison comparisonType)
692 return Compare (value, this, comparisonType) == 0;
695 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
698 throw new ArgumentNullException ("culture");
700 return culture.CompareInfo.Compare (strA, strB, options);
703 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
706 throw new ArgumentNullException ("culture");
711 if (length > (strA.Length - indexA))
712 len1 = strA.Length - indexA;
714 if (length > (strB.Length - indexB))
715 len2 = strB.Length - indexB;
717 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
720 public int CompareTo (Object value)
725 if (!(value is String))
726 throw new ArgumentException ();
728 return String.Compare (this, (String) value);
731 public int CompareTo (String strB)
736 return Compare (this, strB);
739 public static int CompareOrdinal (String strA, String strB)
741 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
744 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
746 if (strA != null && strB != null)
748 if (indexA > strA.Length || indexA < 0)
749 throw new ArgumentOutOfRangeException ("indexA");
750 if (indexB > strB.Length || indexB < 0)
751 throw new ArgumentOutOfRangeException ("indexB");
753 throw new ArgumentOutOfRangeException ("length");
756 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
759 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
761 if (strA != null && strB != null)
763 if (indexA > strA.Length || indexA < 0)
764 throw new ArgumentOutOfRangeException ("indexA");
765 if (indexB > strB.Length || indexB < 0)
766 throw new ArgumentOutOfRangeException ("indexB");
768 throw new ArgumentOutOfRangeException ("length");
771 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
774 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
777 return strB == null ? 0 : -1;
782 int lengthA = Math.Min (lenA, strA.Length - indexA);
783 int lengthB = Math.Min (lenB, strB.Length - indexB);
785 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
788 fixed (char* aptr = strA, bptr = strB) {
789 char* ap = aptr + indexA;
790 char* end = ap + Math.Min (lengthA, lengthB);
791 char* bp = bptr + indexB;
798 return lengthA - lengthB;
803 // Fastest method for internal case insensitive comparison
805 internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
807 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
810 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
812 // Same as above, but checks versus uppercase characters
814 return strB == null ? 0 : -1;
819 int lengthA = Math.Min (lenA, strA.Length - indexA);
820 int lengthB = Math.Min (lenB, strB.Length - indexB);
822 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
825 fixed (char* aptr = strA, bptr = strB) {
826 char* ap = aptr + indexA;
827 char* end = ap + Math.Min (lengthA, lengthB);
828 char* bp = bptr + indexB;
831 char c1 = Char.ToUpperInvariant (*ap);
832 char c2 = Char.ToUpperInvariant (*bp);
839 return lengthA - lengthB;
843 public bool EndsWith (String value)
846 throw new ArgumentNullException ("value");
848 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
851 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
854 throw new ArgumentNullException ("value");
856 culture = CultureInfo.CurrentCulture;
858 return culture.CompareInfo.IsSuffix (this, value,
859 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
862 // Following methods are culture-insensitive
863 public int IndexOfAny (char [] anyOf)
866 throw new ArgumentNullException ();
867 if (this.length == 0)
870 return IndexOfAnyUnchecked (anyOf, 0, this.length);
873 public int IndexOfAny (char [] anyOf, int startIndex)
876 throw new ArgumentNullException ();
877 if (startIndex < 0 || startIndex > this.length)
878 throw new ArgumentOutOfRangeException ();
880 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
883 public int IndexOfAny (char [] anyOf, int startIndex, int count)
886 throw new ArgumentNullException ();
887 if (startIndex < 0 || startIndex > this.length)
888 throw new ArgumentOutOfRangeException ();
889 if (count < 0 || startIndex > this.length - count)
890 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
892 return IndexOfAnyUnchecked (anyOf, startIndex, count);
895 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
897 if (anyOf.Length == 0)
900 if (anyOf.Length == 1)
901 return IndexOfUnchecked (anyOf[0], startIndex, count);
903 fixed (char* any = anyOf) {
907 char* end_any_ptr = any + anyOf.Length;
909 while (++any_ptr != end_any_ptr) {
910 if (*any_ptr > highest) {
915 if (*any_ptr < lowest)
919 fixed (char* start = &start_char) {
920 char* ptr = start + startIndex;
921 char* end_ptr = ptr + count;
923 while (ptr != end_ptr) {
924 if (*ptr > highest || *ptr < lowest) {
930 return (int)(ptr - start);
933 while (++any_ptr != end_any_ptr) {
934 if (*ptr == *any_ptr)
935 return (int)(ptr - start);
946 public int IndexOf (string value, StringComparison comparisonType)
948 return IndexOf (value, 0, this.Length, comparisonType);
951 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
953 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
956 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
958 switch (comparisonType) {
959 case StringComparison.CurrentCulture:
960 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
961 case StringComparison.CurrentCultureIgnoreCase:
962 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
963 case StringComparison.InvariantCulture:
964 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
965 case StringComparison.InvariantCultureIgnoreCase:
966 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
967 case StringComparison.Ordinal:
968 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
969 case StringComparison.OrdinalIgnoreCase:
970 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
972 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
973 throw new ArgumentException (msg, "comparisonType");
977 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
980 throw new ArgumentNullException ("value");
982 throw new ArgumentOutOfRangeException ("startIndex");
983 if (count < 0 || (this.length - startIndex) < count)
984 throw new ArgumentOutOfRangeException ("count");
986 if (options == CompareOptions.Ordinal)
987 return IndexOfOrdinalUnchecked (value, startIndex, count);
988 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
991 internal unsafe int IndexOfOrdinalUnchecked (string value)
993 return IndexOfOrdinalUnchecked (value, 0, length);
996 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
998 int valueLen = value.Length;
999 if (count < valueLen)
1002 if (valueLen <= 1) {
1004 return IndexOfUnchecked (value[0], startIndex, count);
1008 fixed (char* thisptr = this, valueptr = value) {
1009 char* ap = thisptr + startIndex;
1010 char* thisEnd = ap + count - valueLen + 1;
1011 while (ap != thisEnd) {
1012 if (*ap == *valueptr) {
1013 for (int i = 1; i < valueLen; i++) {
1014 if (ap[i] != valueptr[i])
1017 return (int)(ap - thisptr);
1026 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1028 int valueLen = value.Length;
1029 if (count < valueLen)
1035 fixed (char* thisptr = this, valueptr = value) {
1036 char* ap = thisptr + startIndex;
1037 char* thisEnd = ap + count - valueLen + 1;
1038 while (ap != thisEnd) {
1039 for (int i = 0; i < valueLen; i++) {
1040 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1043 return (int)(ap - thisptr);
1051 public int LastIndexOf (string value, StringComparison comparisonType)
1053 if (this.Length == 0)
1054 return value.Length == 0 ? 0 : -1;
1056 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1059 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1061 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1064 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1066 switch (comparisonType) {
1067 case StringComparison.CurrentCulture:
1068 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1069 case StringComparison.CurrentCultureIgnoreCase:
1070 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1071 case StringComparison.InvariantCulture:
1072 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1073 case StringComparison.InvariantCultureIgnoreCase:
1074 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1075 case StringComparison.Ordinal:
1076 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1077 case StringComparison.OrdinalIgnoreCase:
1078 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1080 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1081 throw new ArgumentException (msg, "comparisonType");
1085 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1088 throw new ArgumentNullException ("value");
1089 if (this.Length == 0)
1090 return value.Length == 0 ? 0 : -1;
1091 if (value.Length == 0)
1092 return Math.Min (this.Length - 1, startIndex);
1093 if (startIndex < 0 || startIndex > length)
1094 throw new ArgumentOutOfRangeException ("startIndex");
1095 if (count < 0 || (startIndex < count - 1))
1096 throw new ArgumentOutOfRangeException ("count");
1098 if (options == CompareOptions.Ordinal)
1099 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1100 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1103 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1105 int valueLen = value.Length;
1106 if (count < valueLen)
1109 if (valueLen <= 1) {
1111 return LastIndexOfUnchecked (value[0], startIndex, count);
1115 fixed (char* thisptr = this, valueptr = value) {
1116 char* ap = thisptr + startIndex - valueLen + 1;
1117 char* thisEnd = ap - count + valueLen - 1;
1118 while (ap != thisEnd) {
1119 if (*ap == *valueptr) {
1120 for (int i = 1; i < valueLen; i++) {
1121 if (ap[i] != valueptr[i])
1124 return (int)(ap - thisptr);
1133 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1135 int valueLen = value.Length;
1136 if (count < valueLen)
1142 fixed (char* thisptr = this, valueptr = value) {
1143 char* ap = thisptr + startIndex - valueLen + 1;
1144 char* thisEnd = ap - count + valueLen - 1;
1145 while (ap != thisEnd) {
1146 for (int i = 0; i < valueLen; i++) {
1147 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1150 return (int)(ap - thisptr);
1158 // Following methods are culture-insensitive
1159 public int IndexOf (char value)
1161 if (this.length == 0)
1164 return IndexOfUnchecked (value, 0, this.length);
1167 public int IndexOf (char value, int startIndex)
1170 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1171 if (startIndex > this.length)
1172 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1174 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1177 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1180 public int IndexOf (char value, int startIndex, int count)
1182 if (startIndex < 0 || startIndex > this.length)
1183 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1185 throw new ArgumentOutOfRangeException ("count", "< 0");
1186 if (startIndex > this.length - count)
1187 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1189 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1192 return IndexOfUnchecked (value, startIndex, count);
1195 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1197 // It helps JIT compiler to optimize comparison
1198 int value_32 = (int)value;
1200 fixed (char* start = &start_char) {
1201 char* ptr = start + startIndex;
1202 char* end_ptr = ptr + (count >> 3 << 3);
1204 while (ptr != end_ptr) {
1205 if (*ptr == value_32)
1206 return (int)(ptr - start);
1207 if (ptr[1] == value_32)
1208 return (int)(ptr - start + 1);
1209 if (ptr[2] == value_32)
1210 return (int)(ptr - start + 2);
1211 if (ptr[3] == value_32)
1212 return (int)(ptr - start + 3);
1213 if (ptr[4] == value_32)
1214 return (int)(ptr - start + 4);
1215 if (ptr[5] == value_32)
1216 return (int)(ptr - start + 5);
1217 if (ptr[6] == value_32)
1218 return (int)(ptr - start + 6);
1219 if (ptr[7] == value_32)
1220 return (int)(ptr - start + 7);
1225 end_ptr += count & 0x07;
1226 while (ptr != end_ptr) {
1227 if (*ptr == value_32)
1228 return (int)(ptr - start);
1236 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1240 int end = startIndex + count;
1241 char c = Char.ToUpperInvariant (value);
1242 fixed (char* s = &start_char) {
1243 for (int i = startIndex; i < end; i++)
1244 if (Char.ToUpperInvariant (s [i]) == c)
1250 // Following methods are culture-sensitive
1251 public int IndexOf (String value)
1254 throw new ArgumentNullException ("value");
1255 if (value.Length == 0)
1257 if (this.length == 0)
1259 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.None);
1262 public int IndexOf (String value, int startIndex)
1264 return IndexOf (value, startIndex, this.length - startIndex);
1267 public int IndexOf (String value, int startIndex, int count)
1270 throw new ArgumentNullException ("value");
1271 if (startIndex < 0 || startIndex > this.length)
1272 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1273 if (count < 0 || startIndex > this.length - count)
1274 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1276 if (value.length == 0)
1279 if (startIndex == 0 && this.length == 0)
1285 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1288 // Following methods are culture-insensitive
1289 public int LastIndexOfAny (char [] anyOf)
1292 throw new ArgumentNullException ();
1293 if (this.length == 0)
1296 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1299 public int LastIndexOfAny (char [] anyOf, int startIndex)
1302 throw new ArgumentNullException ();
1303 if (this.length == 0)
1306 if (startIndex < 0 || startIndex >= this.length)
1307 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1309 if (this.length == 0)
1312 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1315 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1318 throw new ArgumentNullException ();
1319 if (this.length == 0)
1322 if ((startIndex < 0) || (startIndex >= this.Length))
1323 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1324 if ((count < 0) || (count > this.Length))
1325 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1326 if (startIndex - count + 1 < 0)
1327 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1329 if (this.length == 0)
1332 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1335 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1337 if (anyOf.Length == 1)
1338 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1340 fixed (char* start = this, testStart = anyOf) {
1341 char* ptr = start + startIndex;
1342 char* ptrEnd = ptr - count;
1344 char* testEnd = testStart + anyOf.Length;
1346 while (ptr != ptrEnd) {
1348 while (test != testEnd) {
1350 return (int)(ptr - start);
1359 // Following methods are culture-insensitive
1360 public int LastIndexOf (char value)
1362 if (this.length == 0)
1365 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1368 public int LastIndexOf (char value, int startIndex)
1370 return LastIndexOf (value, startIndex, startIndex + 1);
1373 public int LastIndexOf (char value, int startIndex, int count)
1375 if (this.length == 0)
1378 // >= for char (> for string)
1379 if ((startIndex < 0) || (startIndex >= this.Length))
1380 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1381 if ((count < 0) || (count > this.Length))
1382 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1383 if (startIndex - count + 1 < 0)
1384 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1386 return LastIndexOfUnchecked (value, startIndex, count);
1389 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1391 // It helps JIT compiler to optimize comparison
1392 int value_32 = (int)value;
1394 fixed (char* start = &start_char) {
1395 char* ptr = start + startIndex;
1396 char* end_ptr = ptr - (count >> 3 << 3);
1398 while (ptr != end_ptr) {
1399 if (*ptr == value_32)
1400 return (int)(ptr - start);
1401 if (ptr[-1] == value_32)
1402 return (int)(ptr - start) - 1;
1403 if (ptr[-2] == value_32)
1404 return (int)(ptr - start) - 2;
1405 if (ptr[-3] == value_32)
1406 return (int)(ptr - start) - 3;
1407 if (ptr[-4] == value_32)
1408 return (int)(ptr - start) - 4;
1409 if (ptr[-5] == value_32)
1410 return (int)(ptr - start) - 5;
1411 if (ptr[-6] == value_32)
1412 return (int)(ptr - start) - 6;
1413 if (ptr[-7] == value_32)
1414 return (int)(ptr - start) - 7;
1419 end_ptr -= count & 0x07;
1420 while (ptr != end_ptr) {
1421 if (*ptr == value_32)
1422 return (int)(ptr - start);
1430 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1434 int end = startIndex - count;
1435 char c = Char.ToUpperInvariant (value);
1436 fixed (char* s = &start_char) {
1437 for (int i = startIndex; i > end; i--)
1438 if (Char.ToUpperInvariant (s [i]) == c)
1444 // Following methods are culture-sensitive
1445 public int LastIndexOf (String value)
1447 return LastIndexOf (value, this.length - 1, this.length);
1450 public int LastIndexOf (String value, int startIndex)
1452 int max = startIndex;
1453 if (max < this.Length)
1455 return LastIndexOf (value, startIndex, max);
1458 public int LastIndexOf (String value, int startIndex, int count)
1461 throw new ArgumentNullException ("value");
1463 if (this.length == 0)
1464 return value.Length == 0 ? 0 : -1;
1465 // -1 > startIndex > for string (0 > startIndex >= for char)
1466 if ((startIndex < -1) || (startIndex > this.Length))
1467 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1468 if ((count < 0) || (count > this.Length))
1469 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1470 if (startIndex - count + 1 < 0)
1471 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1473 if (value.Length == 0)
1474 return Math.Min (this.Length - 1, startIndex);
1476 if (startIndex == 0 && this.length == 0)
1479 // This check is needed to match undocumented MS behaviour
1480 if (this.length == 0 && value.length > 0)
1486 if (startIndex == this.Length)
1488 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1491 public bool Contains (String value)
1494 throw new ArgumentNullException ("value");
1496 return IndexOfOrdinalUnchecked (value, 0, Length) != -1;
1499 public static bool IsNullOrEmpty (String value)
1501 return (value == null) || (value.Length == 0);
1504 public string Normalize ()
1506 return Normalization.Normalize (this, 0);
1509 public string Normalize (NormalizationForm normalizationForm)
1511 switch (normalizationForm) {
1513 return Normalization.Normalize (this, 0);
1514 case NormalizationForm.FormD:
1515 return Normalization.Normalize (this, 1);
1516 case NormalizationForm.FormKC:
1517 return Normalization.Normalize (this, 2);
1518 case NormalizationForm.FormKD:
1519 return Normalization.Normalize (this, 3);
1523 public bool IsNormalized ()
1525 return Normalization.IsNormalized (this, 0);
1528 public bool IsNormalized (NormalizationForm normalizationForm)
1530 switch (normalizationForm) {
1532 return Normalization.IsNormalized (this, 0);
1533 case NormalizationForm.FormD:
1534 return Normalization.IsNormalized (this, 1);
1535 case NormalizationForm.FormKC:
1536 return Normalization.IsNormalized (this, 2);
1537 case NormalizationForm.FormKD:
1538 return Normalization.IsNormalized (this, 3);
1542 public string Remove (int startIndex)
1545 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1546 if (startIndex >= this.length)
1547 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1549 return Remove (startIndex, this.length - startIndex);
1552 public String PadLeft (int totalWidth)
1554 return PadLeft (totalWidth, ' ');
1557 public unsafe String PadLeft (int totalWidth, char paddingChar)
1559 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1562 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1564 if (totalWidth < this.length)
1566 if (totalWidth == 0)
1569 String tmp = InternalAllocateStr (totalWidth);
1571 fixed (char* dest = tmp, src = this) {
1572 char* padPos = dest;
1575 padTo = checked (dest + (totalWidth - length));
1576 } catch (OverflowException) {
1577 throw new OutOfMemoryException ();
1580 while (padPos != padTo)
1581 *padPos++ = paddingChar;
1583 CharCopy (padTo, src, length);
1588 public String PadRight (int totalWidth)
1590 return PadRight (totalWidth, ' ');
1593 public unsafe String PadRight (int totalWidth, char paddingChar)
1595 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1598 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1600 if (totalWidth < this.length)
1602 if (totalWidth == 0)
1605 String tmp = InternalAllocateStr (totalWidth);
1607 fixed (char* dest = tmp, src = this) {
1608 CharCopy (dest, src, length);
1611 char* padPos = checked (dest + length);
1612 char* padTo = checked (dest + totalWidth);
1613 while (padPos != padTo)
1614 *padPos++ = paddingChar;
1615 } catch (OverflowException) {
1616 throw new OutOfMemoryException ();
1622 public bool StartsWith (String value)
1625 throw new ArgumentNullException ("value");
1627 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1630 [ComVisible (false)]
1631 public bool StartsWith (string value, StringComparison comparisonType)
1634 throw new ArgumentNullException ("value");
1636 switch (comparisonType) {
1637 case StringComparison.CurrentCulture:
1638 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1639 case StringComparison.CurrentCultureIgnoreCase:
1640 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1641 case StringComparison.InvariantCulture:
1642 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1643 case StringComparison.InvariantCultureIgnoreCase:
1644 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1645 case StringComparison.Ordinal:
1646 return StartsWithOrdinalUnchecked (value);
1647 case StringComparison.OrdinalIgnoreCase:
1648 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1650 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1651 throw new ArgumentException (msg, "comparisonType");
1655 internal bool StartsWithOrdinalUnchecked (string value)
1657 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1660 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1662 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1665 [ComVisible (false)]
1666 public bool EndsWith (string value, StringComparison comparisonType)
1669 throw new ArgumentNullException ("value");
1671 switch (comparisonType) {
1672 case StringComparison.CurrentCulture:
1673 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1674 case StringComparison.CurrentCultureIgnoreCase:
1675 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1676 case StringComparison.InvariantCulture:
1677 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1678 case StringComparison.InvariantCultureIgnoreCase:
1679 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1680 case StringComparison.Ordinal:
1681 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1682 case StringComparison.OrdinalIgnoreCase:
1683 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1685 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1686 throw new ArgumentException (msg, "comparisonType");
1690 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1692 if (culture == null)
1693 culture = CultureInfo.CurrentCulture;
1695 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1698 // Following method is culture-insensitive
1699 public unsafe String Replace (char oldChar, char newChar)
1701 if (this.length == 0 || oldChar == newChar)
1704 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1705 if (start_pos == -1)
1711 string tmp = InternalAllocateStr (length);
1712 fixed (char* dest = tmp, src = &start_char) {
1714 CharCopy (dest, src, start_pos);
1716 char* end_ptr = dest + length;
1717 char* dest_ptr = dest + start_pos;
1718 char* src_ptr = src + start_pos;
1720 while (dest_ptr != end_ptr) {
1721 if (*src_ptr == oldChar)
1722 *dest_ptr = newChar;
1724 *dest_ptr = *src_ptr;
1733 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1734 public String Replace (String oldValue, String newValue)
1736 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1737 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1739 if (oldValue == null)
1740 throw new ArgumentNullException ("oldValue");
1742 if (oldValue.Length == 0)
1743 throw new ArgumentException ("oldValue is the empty string.");
1745 if (this.Length == 0)
1748 if (newValue == null)
1751 return ReplaceUnchecked (oldValue, newValue);
1754 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1756 if (oldValue.length > length)
1758 if (oldValue.length == 1 && newValue.length == 1) {
1759 return Replace (oldValue[0], newValue[0]);
1760 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1761 // because the length of the result would be this.length and length calculation unneccesary
1764 const int maxValue = 200; // Allocate 800 byte maximum
1765 int* dat = stackalloc int[maxValue];
1766 fixed (char* source = this, replace = newValue) {
1767 int i = 0, count = 0;
1768 while (i < length) {
1769 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1773 if (count < maxValue)
1774 dat[count++] = found;
1776 return ReplaceFallback (oldValue, newValue, maxValue);
1778 i = found + oldValue.length;
1785 nlen = this.length + ((newValue.length - oldValue.length) * count);
1786 } catch (OverflowException) {
1787 throw new OutOfMemoryException ();
1790 String tmp = InternalAllocateStr (nlen);
1792 int curPos = 0, lastReadPos = 0;
1793 fixed (char* dest = tmp) {
1794 for (int j = 0; j < count; j++) {
1795 int precopy = dat[j] - lastReadPos;
1796 CharCopy (dest + curPos, source + lastReadPos, precopy);
1798 lastReadPos = dat[j] + oldValue.length;
1799 CharCopy (dest + curPos, replace, newValue.length);
1800 curPos += newValue.length;
1802 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1808 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1810 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1811 StringBuilder sb = new StringBuilder (lengthEstimate);
1812 for (int i = 0; i < length;) {
1813 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1815 sb.Append (SubstringUnchecked (i, length - i));
1818 sb.Append (SubstringUnchecked (i, found - i));
1819 sb.Append (newValue);
1820 i = found + oldValue.Length;
1822 return sb.ToString ();
1826 public unsafe String Remove (int startIndex, int count)
1829 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1831 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1832 if (startIndex > this.length - count)
1833 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1835 String tmp = InternalAllocateStr (this.length - count);
1837 fixed (char *dest = tmp, src = this) {
1839 CharCopy (dst, src, startIndex);
1840 int skip = startIndex + count;
1842 CharCopy (dst, src + skip, length - skip);
1847 public String ToLower ()
1849 return ToLower (CultureInfo.CurrentCulture);
1852 public String ToLower (CultureInfo culture)
1854 if (culture == null)
1855 throw new ArgumentNullException ("culture");
1857 if (culture.LCID == 0x007F) // Invariant
1858 return ToLowerInvariant ();
1860 return culture.TextInfo.ToLower (this);
1863 public unsafe String ToLowerInvariant ()
1868 string tmp = InternalAllocateStr (length);
1869 fixed (char* source = &start_char, dest = tmp) {
1871 char* destPtr = (char*)dest;
1872 char* sourcePtr = (char*)source;
1874 for (int n = 0; n < length; n++) {
1875 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1883 public String ToUpper ()
1885 return ToUpper (CultureInfo.CurrentCulture);
1888 public String ToUpper (CultureInfo culture)
1890 if (culture == null)
1891 throw new ArgumentNullException ("culture");
1893 if (culture.LCID == 0x007F) // Invariant
1894 return ToUpperInvariant ();
1896 return culture.TextInfo.ToUpper (this);
1899 public unsafe String ToUpperInvariant ()
1904 string tmp = InternalAllocateStr (length);
1905 fixed (char* source = &start_char, dest = tmp) {
1907 char* destPtr = (char*)dest;
1908 char* sourcePtr = (char*)source;
1910 for (int n = 0; n < length; n++) {
1911 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1919 public override String ToString ()
1924 public String ToString (IFormatProvider provider)
1929 public static String Format (String format, Object arg0)
1931 return Format (null, format, new Object[] {arg0});
1934 public static String Format (String format, Object arg0, Object arg1)
1936 return Format (null, format, new Object[] {arg0, arg1});
1939 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1941 return Format (null, format, new Object[] {arg0, arg1, arg2});
1944 public static string Format (string format, params object[] args)
1946 return Format (null, format, args);
1949 public static string Format (IFormatProvider provider, string format, params object[] args)
1951 StringBuilder b = FormatHelper (null, provider, format, args);
1952 return b.ToString ();
1955 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1958 throw new ArgumentNullException ("format");
1960 throw new ArgumentNullException ("args");
1962 if (result == null) {
1963 /* Try to approximate the size of result to avoid reallocations */
1967 for (i = 0; i < args.Length; ++i) {
1968 string s = args [i] as string;
1974 if (i == args.Length)
1975 result = new StringBuilder (len + format.length);
1977 result = new StringBuilder ();
1982 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1984 while (ptr < format.length) {
1985 char c = format[ptr ++];
1988 result.Append (format, start, ptr - start - 1);
1990 // check for escaped open bracket
1992 if (format[ptr] == '{') {
2003 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2004 if (n >= args.Length)
2005 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2009 object arg = args[n];
2014 else if (formatter != null)
2015 str = formatter.Format (arg_format, arg, provider);
2020 if (arg is IFormattable)
2021 str = ((IFormattable)arg).ToString (arg_format, provider);
2023 str = arg.ToString () ?? Empty;
2026 // pad formatted string and append to result
2027 if (width > str.length) {
2028 const char padchar = ' ';
2029 int padlen = width - str.length;
2032 result.Append (str);
2033 result.Append (padchar, padlen);
2036 result.Append (padchar, padlen);
2037 result.Append (str);
2040 result.Append (str);
2045 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2046 result.Append (format, start, ptr - start - 1);
2049 else if (c == '}') {
2050 throw new FormatException ("Input string was not in a correct format.");
2054 if (start < format.length)
2055 result.Append (format, start, format.Length - start);
2060 public unsafe static String Copy (String str)
2063 throw new ArgumentNullException ("str");
2065 int length = str.length;
2067 String tmp = InternalAllocateStr (length);
2069 fixed (char *dest = tmp, src = str) {
2070 CharCopy (dest, src, length);
2076 public static String Concat (Object arg0)
2081 return arg0.ToString ();
2084 public static String Concat (Object arg0, Object arg1)
2086 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2089 public static String Concat (Object arg0, Object arg1, Object arg2)
2095 s1 = arg0.ToString ();
2100 s2 = arg1.ToString ();
2105 s3 = arg2.ToString ();
2107 return Concat (s1, s2, s3);
2110 [CLSCompliant(false)]
2111 public static String Concat (Object arg0, Object arg1, Object arg2,
2112 Object arg3, __arglist)
2114 string s1, s2, s3, s4;
2119 s1 = arg0.ToString ();
2124 s2 = arg1.ToString ();
2129 s3 = arg2.ToString ();
2131 ArgIterator iter = new ArgIterator (__arglist);
2132 int argCount = iter.GetRemainingCount();
2134 StringBuilder sb = new StringBuilder ();
2136 sb.Append (arg3.ToString ());
2138 for (int i = 0; i < argCount; i++) {
2139 TypedReference typedRef = iter.GetNextArg ();
2140 sb.Append (TypedReference.ToObject (typedRef));
2143 s4 = sb.ToString ();
2145 return Concat (s1, s2, s3, s4);
2148 public unsafe static String Concat (String str0, String str1)
2150 if (str0 == null || str0.Length == 0) {
2151 if (str1 == null || str1.Length == 0)
2156 if (str1 == null || str1.Length == 0)
2159 int nlen = str0.length + str1.length;
2161 throw new OutOfMemoryException ();
2162 String tmp = InternalAllocateStr (nlen);
2164 fixed (char *dest = tmp, src = str0)
2165 CharCopy (dest, src, str0.length);
2166 fixed (char *dest = tmp, src = str1)
2167 CharCopy (dest + str0.Length, src, str1.length);
2172 public unsafe static String Concat (String str0, String str1, String str2)
2174 if (str0 == null || str0.Length == 0){
2175 if (str1 == null || str1.Length == 0){
2176 if (str2 == null || str2.Length == 0)
2180 if (str2 == null || str2.Length == 0)
2185 if (str1 == null || str1.Length == 0){
2186 if (str2 == null || str2.Length == 0)
2191 if (str2 == null || str2.Length == 0)
2196 int nlen = str0.length + str1.length;
2198 throw new OutOfMemoryException ();
2199 nlen += str2.length;
2201 throw new OutOfMemoryException ();
2202 String tmp = InternalAllocateStr (nlen);
2204 if (str0.Length != 0) {
2205 fixed (char *dest = tmp, src = str0) {
2206 CharCopy (dest, src, str0.length);
2209 if (str1.Length != 0) {
2210 fixed (char *dest = tmp, src = str1) {
2211 CharCopy (dest + str0.Length, src, str1.length);
2214 if (str2.Length != 0) {
2215 fixed (char *dest = tmp, src = str2) {
2216 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2223 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2225 if (str0 == null && str1 == null && str2 == null && str3 == null)
2237 int nlen = str0.length + str1.length;
2239 throw new OutOfMemoryException ();
2240 nlen += str2.length;
2242 throw new OutOfMemoryException ();
2243 nlen += str3.length;
2245 throw new OutOfMemoryException ();
2246 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2248 if (str0.Length != 0) {
2249 fixed (char *dest = tmp, src = str0) {
2250 CharCopy (dest, src, str0.length);
2253 if (str1.Length != 0) {
2254 fixed (char *dest = tmp, src = str1) {
2255 CharCopy (dest + str0.Length, src, str1.length);
2258 if (str2.Length != 0) {
2259 fixed (char *dest = tmp, src = str2) {
2260 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2263 if (str3.Length != 0) {
2264 fixed (char *dest = tmp, src = str3) {
2265 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2272 public static String Concat (params Object[] args)
2275 throw new ArgumentNullException ("args");
2277 int argLen = args.Length;
2281 string [] strings = new string [argLen];
2283 for (int i = 0; i < argLen; i++) {
2284 if (args[i] != null) {
2285 strings[i] = args[i].ToString ();
2286 len += strings[i].length;
2288 throw new OutOfMemoryException ();
2292 return ConcatInternal (strings, len);
2295 public static String Concat (params String[] values)
2298 throw new ArgumentNullException ("values");
2301 for (int i = 0; i < values.Length; i++) {
2302 String s = values[i];
2306 throw new OutOfMemoryException ();
2309 return ConcatInternal (values, len);
2312 private static unsafe String ConcatInternal (String[] values, int length)
2317 throw new OutOfMemoryException ();
2319 String tmp = InternalAllocateStr (length);
2321 fixed (char* dest = tmp) {
2323 for (int i = 0; i < values.Length; i++) {
2324 String source = values[i];
2325 if (source != null) {
2326 fixed (char* src = source) {
2327 CharCopy (dest + pos, src, source.length);
2329 pos += source.Length;
2336 public unsafe String Insert (int startIndex, String value)
2339 throw new ArgumentNullException ("value");
2341 if (startIndex < 0 || startIndex > this.length)
2342 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2344 if (value.Length == 0)
2346 if (this.Length == 0)
2349 int nlen = this.length + value.length;
2351 throw new OutOfMemoryException ();
2353 String tmp = InternalAllocateStr (nlen);
2355 fixed (char *dest = tmp, src = this, val = value) {
2357 CharCopy (dst, src, startIndex);
2359 CharCopy (dst, val, value.length);
2360 dst += value.length;
2361 CharCopy (dst, src + startIndex, length - startIndex);
2366 public static string Intern (string str)
2369 throw new ArgumentNullException ("str");
2371 return InternalIntern (str);
2374 public static string IsInterned (string str)
2377 throw new ArgumentNullException ("str");
2379 return InternalIsInterned (str);
2382 public static string Join (string separator, params string [] value)
2385 throw new ArgumentNullException ("value");
2386 if (separator == null)
2389 return JoinUnchecked (separator, value, 0, value.Length);
2392 public static string Join (string separator, string[] value, int startIndex, int count)
2395 throw new ArgumentNullException ("value");
2397 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2399 throw new ArgumentOutOfRangeException ("count", "< 0");
2400 if (startIndex > value.Length - count)
2401 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2403 if (startIndex == value.Length)
2405 if (separator == null)
2408 return JoinUnchecked (separator, value, startIndex, count);
2411 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2413 // Unchecked parameters
2414 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2415 // separator and value must not be null
2418 int maxIndex = startIndex + count;
2419 // Precount the number of characters that the resulting string will have
2420 for (int i = startIndex; i < maxIndex; i++) {
2421 String s = value[i];
2425 length += separator.length * (count - 1);
2429 String tmp = InternalAllocateStr (length);
2432 fixed (char* dest = tmp, sepsrc = separator) {
2433 // Copy each string from value except the last one and add a separator for each
2435 for (int i = startIndex; i < maxIndex; i++) {
2436 String source = value[i];
2437 if (source != null) {
2438 if (source.Length > 0) {
2439 fixed (char* src = source)
2440 CharCopy (dest + pos, src, source.Length);
2441 pos += source.Length;
2444 if (separator.Length > 0) {
2445 CharCopy (dest + pos, sepsrc, separator.Length);
2446 pos += separator.Length;
2449 // Append last string that does not get an additional separator
2450 String sourceLast = value[maxIndex];
2451 if (sourceLast != null) {
2452 if (sourceLast.Length > 0) {
2453 fixed (char* src = sourceLast)
2454 CharCopy (dest + pos, src, sourceLast.Length);
2461 bool IConvertible.ToBoolean (IFormatProvider provider)
2463 return Convert.ToBoolean (this, provider);
2466 byte IConvertible.ToByte (IFormatProvider provider)
2468 return Convert.ToByte (this, provider);
2471 char IConvertible.ToChar (IFormatProvider provider)
2473 return Convert.ToChar (this, provider);
2476 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2478 return Convert.ToDateTime (this, provider);
2481 decimal IConvertible.ToDecimal (IFormatProvider provider)
2483 return Convert.ToDecimal (this, provider);
2486 double IConvertible.ToDouble (IFormatProvider provider)
2488 return Convert.ToDouble (this, provider);
2491 short IConvertible.ToInt16 (IFormatProvider provider)
2493 return Convert.ToInt16 (this, provider);
2496 int IConvertible.ToInt32 (IFormatProvider provider)
2498 return Convert.ToInt32 (this, provider);
2501 long IConvertible.ToInt64 (IFormatProvider provider)
2503 return Convert.ToInt64 (this, provider);
2506 sbyte IConvertible.ToSByte (IFormatProvider provider)
2508 return Convert.ToSByte (this, provider);
2511 float IConvertible.ToSingle (IFormatProvider provider)
2513 return Convert.ToSingle (this, provider);
2516 object IConvertible.ToType (Type type, IFormatProvider provider)
2518 return Convert.DefaultToType ((IConvertible)this, type, provider);
2521 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2523 return Convert.ToUInt16 (this, provider);
2526 uint IConvertible.ToUInt32 (IFormatProvider provider)
2528 return Convert.ToUInt32 (this, provider);
2531 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2533 return Convert.ToUInt64 (this, provider);
2542 public CharEnumerator GetEnumerator ()
2544 return new CharEnumerator (this);
2547 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2549 return new CharEnumerator (this);
2552 IEnumerator IEnumerable.GetEnumerator ()
2554 return new CharEnumerator (this);
2557 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2558 out bool left_align, out string format)
2560 int max = str.Length;
2562 // parses format specifier of form:
2566 // N = argument number (non-negative integer)
2568 n = ParseDecimal (str, ref ptr);
2570 throw new FormatException ("Input string was not in a correct format.");
2572 // M = width (non-negative integer)
2574 if (ptr < max && str[ptr] == ',') {
2575 // White space between ',' and number or sign.
2577 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2581 format = str.Substring (start, ptr - start);
2583 left_align = (ptr < max && str [ptr] == '-');
2587 width = ParseDecimal (str, ref ptr);
2589 throw new FormatException ("Input string was not in a correct format.");
2597 // F = argument format (string)
2599 if (ptr < max && str[ptr] == ':') {
2602 if (str [ptr] == '}') {
2603 if (ptr + 1 < max && str [ptr + 1] == '}') {
2605 format += str.Substring (start, ptr - start);
2617 format += str.Substring (start, ptr - start);
2622 if ((ptr >= max) || str[ptr ++] != '}')
2623 throw new FormatException ("Input string was not in a correct format.");
2626 private static int ParseDecimal (string str, ref int ptr)
2630 int max = str.Length;
2634 if (c < '0' || '9' < c)
2637 n = n * 10 + c - '0';
2641 if (p == ptr || p == max)
2648 internal unsafe void InternalSetChar (int idx, char val)
2650 if ((uint) idx >= (uint) Length)
2651 throw new ArgumentOutOfRangeException ("idx");
2653 fixed (char * pStr = &start_char)
2659 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2660 internal extern void InternalSetLength (int newLength);
2662 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2663 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2664 public unsafe override int GetHashCode ()
2666 fixed (char * c = this) {
2668 char * end = cc + length - 1;
2670 for (;cc < end; cc += 2) {
2671 h = (h << 5) - h + *cc;
2672 h = (h << 5) - h + cc [1];
2676 h = (h << 5) - h + *cc;
2682 public static string Concat (IEnumerable<string> values)
2685 throw new ArgumentNullException ("values");
2687 var stringList = new List<string> ();
2689 foreach (var v in values){
2694 throw new OutOfMemoryException ();
2697 return ConcatInternal (stringList.ToArray (), len);
2700 [ComVisibleAttribute(false)]
2701 public static string Concat<T> (IEnumerable<T> values)
2704 throw new ArgumentNullException ("values");
2706 var stringList = new List<string> ();
2708 foreach (var v in values){
2709 string sr = v.ToString ();
2712 throw new OutOfMemoryException ();
2713 stringList.Add (sr);
2715 return ConcatInternal (stringList.ToArray (), len);
2718 [ComVisibleAttribute(false)]
2719 public static string Join (string separator, IEnumerable<string> values)
2721 if (separator == null)
2722 return Concat (values);
2725 throw new ArgumentNullException ("values");
2727 var stringList = new List<string> (values);
2729 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2732 [ComVisibleAttribute(false)]
2733 public static string Join (string separator, params object [] values)
2735 if (separator == null)
2736 return Concat (values);
2739 throw new ArgumentNullException ("values");
2741 var strCopy = new string [values.Length];
2743 foreach (var v in values)
2744 strCopy [i++] = v.ToString ();
2746 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2749 [ComVisible (false)]
2750 public static string Join<T> (string separator, IEnumerable<T> values)
2752 if (separator == null)
2753 return Concat<T> (values);
2756 throw new ArgumentNullException ("values");
2758 var stringList = values as IList<T> ?? new List<T> (values);
2759 var strCopy = new string [stringList.Count];
2761 foreach (var v in stringList)
2762 strCopy [i++] = v.ToString ();
2764 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2767 public static bool IsNullOrWhiteSpace (string value)
2769 if ((value == null) || (value.Length == 0))
2771 foreach (char c in value)
2772 if (!Char.IsWhiteSpace (c))
2777 internal unsafe int GetCaseInsensitiveHashCode ()
2779 fixed (char * c = this) {
2781 char * end = cc + length - 1;
2783 for (;cc < end; cc += 2) {
2784 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2785 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2789 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2794 // Certain constructors are redirected to CreateString methods with
2795 // matching argument list. The this pointer should not be used.
2797 private unsafe String CreateString (sbyte* value)
2802 byte* bytes = (byte*) value;
2806 while (bytes++ [0] != 0)
2808 } catch (NullReferenceException) {
2809 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2812 return CreateString (value, 0, length, null);
2815 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2817 return CreateString (value, startIndex, length, null);
2820 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2823 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2825 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2826 if (value + startIndex < value)
2827 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2831 throw new ArgumentNullException ("value");
2835 enc = Encoding.Default;
2838 byte [] bytes = new byte [length];
2841 fixed (byte* bytePtr = bytes)
2844 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2845 memcpy (bytePtr, (byte*) (value + startIndex), length);
2846 } catch (NullReferenceException) {
2847 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2850 // GetString () is called even when length == 0
2851 return enc.GetString (bytes);
2854 unsafe string CreateString (char *value)
2864 string result = InternalAllocateStr (i);
2867 fixed (char *dest = result) {
2868 CharCopy (dest, value, i);
2874 unsafe string CreateString (char *value, int startIndex, int length)
2879 throw new ArgumentNullException ("value");
2881 throw new ArgumentOutOfRangeException ("startIndex");
2883 throw new ArgumentOutOfRangeException ("length");
2885 string result = InternalAllocateStr (length);
2887 fixed (char *dest = result) {
2888 CharCopy (dest, value + startIndex, length);
2893 unsafe string CreateString (char [] val, int startIndex, int length)
2896 throw new ArgumentNullException ("value");
2898 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2900 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2901 if (startIndex > val.Length - length)
2902 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2906 string result = InternalAllocateStr (length);
2908 fixed (char *dest = result, src = val) {
2909 CharCopy (dest, src + startIndex, length);
2914 unsafe string CreateString (char [] val)
2916 if (val == null || val.Length == 0)
2918 string result = InternalAllocateStr (val.Length);
2920 fixed (char *dest = result, src = val) {
2921 CharCopy (dest, src, val.Length);
2926 unsafe string CreateString (char c, int count)
2929 throw new ArgumentOutOfRangeException ("count");
2932 string result = InternalAllocateStr (count);
2933 fixed (char *dest = result) {
2935 char *end = p + count;
2944 /* helpers used by the runtime as well as above or eslewhere in corlib */
2945 internal static unsafe void memset (byte *dest, int val, int len)
2956 val = val | (val << 8);
2957 val = val | (val << 16);
2960 int rest = (int)dest & 3;
2968 } while (rest != 0);
2971 ((int*)dest) [0] = val;
2972 ((int*)dest) [1] = val;
2973 ((int*)dest) [2] = val;
2974 ((int*)dest) [3] = val;
2979 ((int*)dest) [0] = val;
2991 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2992 /*while (size >= 32) {
2993 // using long is better than int and slower than double
2994 // FIXME: enable this only on correct alignment or on platforms
2995 // that can tolerate unaligned reads/writes of doubles
2996 ((double*)dest) [0] = ((double*)src) [0];
2997 ((double*)dest) [1] = ((double*)src) [1];
2998 ((double*)dest) [2] = ((double*)src) [2];
2999 ((double*)dest) [3] = ((double*)src) [3];
3004 while (size >= 16) {
3005 ((int*)dest) [0] = ((int*)src) [0];
3006 ((int*)dest) [1] = ((int*)src) [1];
3007 ((int*)dest) [2] = ((int*)src) [2];
3008 ((int*)dest) [3] = ((int*)src) [3];
3014 ((int*)dest) [0] = ((int*)src) [0];
3020 ((byte*)dest) [0] = ((byte*)src) [0];
3026 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3028 ((short*)dest) [0] = ((short*)src) [0];
3029 ((short*)dest) [1] = ((short*)src) [1];
3030 ((short*)dest) [2] = ((short*)src) [2];
3031 ((short*)dest) [3] = ((short*)src) [3];
3037 ((short*)dest) [0] = ((short*)src) [0];
3043 ((byte*)dest) [0] = ((byte*)src) [0];
3045 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3047 ((byte*)dest) [0] = ((byte*)src) [0];
3048 ((byte*)dest) [1] = ((byte*)src) [1];
3049 ((byte*)dest) [2] = ((byte*)src) [2];
3050 ((byte*)dest) [3] = ((byte*)src) [3];
3051 ((byte*)dest) [4] = ((byte*)src) [4];
3052 ((byte*)dest) [5] = ((byte*)src) [5];
3053 ((byte*)dest) [6] = ((byte*)src) [6];
3054 ((byte*)dest) [7] = ((byte*)src) [7];
3060 ((byte*)dest) [0] = ((byte*)src) [0];
3061 ((byte*)dest) [1] = ((byte*)src) [1];
3067 ((byte*)dest) [0] = ((byte*)src) [0];
3070 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3071 // FIXME: if pointers are not aligned, try to align them
3072 // so a faster routine can be used. Handle the case where
3073 // the pointers can't be reduced to have the same alignment
3074 // (just ignore the issue on x86?)
3075 if ((((int)dest | (int)src) & 3) != 0) {
3076 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3082 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3083 ((short*)dest) [0] = ((short*)src) [0];
3088 if ((((int)dest | (int)src) & 1) != 0) {
3089 memcpy1 (dest, src, size);
3092 if ((((int)dest | (int)src) & 2) != 0) {
3093 memcpy2 (dest, src, size);
3097 memcpy4 (dest, src, size);
3100 /* Used by the runtime */
3101 internal static unsafe void bzero (byte *dest, int len) {
3102 memset (dest, 0, len);
3105 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3106 ((byte*)dest) [0] = 0;
3109 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3110 ((short*)dest) [0] = 0;
3113 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3114 ((int*)dest) [0] = 0;
3117 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3118 ((long*)dest) [0] = 0;
3121 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3122 ((byte*)dest) [0] = ((byte*)src) [0];
3125 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3126 ((short*)dest) [0] = ((short*)src) [0];
3129 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3130 ((int*)dest) [0] = ((int*)src) [0];
3133 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3134 ((long*)dest) [0] = ((long*)src) [0];
3137 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3138 // Same rules as for memcpy, but with the premise that
3139 // chars can only be aligned to even addresses if their
3140 // enclosing types are correctly aligned
3141 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3142 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3143 ((short*)dest) [0] = ((short*)src) [0];
3148 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3149 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3153 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3156 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3160 for (int i = count; i > 0; i--) {
3167 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3169 fixed (char* dest = target, src = source)
3170 CharCopy (dest + targetIndex, src + sourceIndex, count);
3173 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3175 fixed (char* dest = target, src = source)
3176 CharCopy (dest + targetIndex, src + sourceIndex, count);
3179 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3180 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3182 fixed (char* dest = target, src = source)
3183 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3186 internal static String FastAllocateString (int length)
3188 return InternalAllocateStr (length);
3191 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3192 unsafe public extern String (char *value);
3194 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3195 unsafe public extern String (char *value, int startIndex, int length);
3197 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3198 unsafe public extern String (sbyte *value);
3200 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3201 unsafe public extern String (sbyte *value, int startIndex, int length);
3203 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3204 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3206 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3207 public extern String (char [] value, int startIndex, int length);
3209 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3210 public extern String (char [] value);
3212 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3213 public extern String (char c, int count);
3215 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3216 internal extern static String InternalAllocateStr (int length);
3218 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3219 private extern static string InternalIntern (string str);
3221 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3222 private extern static string InternalIsInterned (string str);
3224 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3225 private extern static int GetLOSLimit ();
3227 #region "from referencesource" // and actually we replaced some parts.
3229 // Helper for encodings so they can talk to our buffer directly
3230 // stringLength must be the exact size we'll expect
3231 [System.Security.SecurityCritical] // auto-generated
3232 unsafe static internal String CreateStringFromEncoding(
3233 byte* bytes, int byteLength, Encoding encoding)
3235 Contract.Requires(bytes != null);
3236 Contract.Requires(byteLength >= 0);
3238 // Get our string length
3239 int stringLength = encoding.GetCharCount(bytes, byteLength, null);
3240 Contract.Assert(stringLength >= 0, "stringLength >= 0");
3242 // They gave us an empty string if they needed one
3243 // 0 bytelength might be possible if there's something in an encoder
3244 if (stringLength == 0)
3245 return String.Empty;
3247 String s = FastAllocateString(stringLength);
3248 fixed(char* pTempChars = &s.start_char)
3250 int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
3251 Contract.Assert(stringLength == doubleCheck,
3252 "Expected encoding.GetChars to return same length as encoding.GetCharCount");
3258 // our own implementation for CLR icall.
3259 unsafe internal static int nativeCompareOrdinalIgnoreCaseWC (string name, sbyte *strBBytes)
3261 for (int i = 0; i < name.Length; i++) {
3262 sbyte b = *(strBBytes + i);
3264 throw new ArgumentException ();
3265 int ret = char.ToUpper ((char) b) - char.ToUpper (name [i]);