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 public static unsafe bool Equals (string a, string b)
71 if ((a as object) == (b as object))
74 if (a == null || b == null)
82 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
87 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
88 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
89 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
90 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
99 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
100 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
109 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
117 return len == 0 || *s1_ptr == *s2_ptr;
121 public static bool operator == (String a, String b)
123 return Equals (a, b);
126 public static bool operator != (String a, String b)
128 return !Equals (a, b);
131 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
132 public override bool Equals (Object obj)
134 return Equals (this, obj as String);
137 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public bool Equals (String value)
140 return Equals (this, value);
143 [IndexerName ("Chars")]
144 public unsafe char this [int index] {
146 if (index < 0 || index >= length)
147 throw new IndexOutOfRangeException ();
148 fixed (char* c = &start_char)
153 public Object Clone ()
158 public TypeCode GetTypeCode ()
160 return TypeCode.String;
163 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
165 if (destination == null)
166 throw new ArgumentNullException ("destination");
168 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
169 if (destinationIndex < 0)
170 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
172 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
173 if (sourceIndex > Length - count)
174 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
175 if (destinationIndex > destination.Length - count)
176 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
178 fixed (char* dest = destination, src = this)
179 CharCopy (dest + destinationIndex, src + sourceIndex, count);
182 public char[] ToCharArray ()
184 return ToCharArray (0, length);
187 public unsafe char[] ToCharArray (int startIndex, int length)
190 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
192 throw new ArgumentOutOfRangeException ("length", "< 0");
193 if (startIndex > this.length - length)
194 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
196 char[] tmp = new char [length];
197 fixed (char* dest = tmp, src = this)
198 CharCopy (dest, src + startIndex, length);
202 public String [] Split (params char [] separator)
204 return Split (separator, int.MaxValue, 0);
207 public String[] Split (char[] separator, int count)
209 return Split (separator, count, 0);
213 public String[] Split (char[] separator, StringSplitOptions options)
215 return Split (separator, Int32.MaxValue, options);
219 public String[] Split (char[] separator, int count, StringSplitOptions options)
222 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
223 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
224 throw new ArgumentException ("Illegal enum value: " + options + ".");
226 if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
227 return EmptyArray<string>.Value;
231 EmptyArray<string>.Value :
232 new String[1] { this };
235 return SplitByCharacters (separator, count, options != 0);
239 public String[] Split (string[] separator, StringSplitOptions options)
241 return Split (separator, Int32.MaxValue, options);
245 public String[] Split (string[] separator, int count, StringSplitOptions options)
248 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
249 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
250 throw new ArgumentException ("Illegal enum value: " + options + ".");
254 EmptyArray<string>.Value :
255 new String[1] { this };
258 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
260 if (separator == null || separator.Length == 0)
261 return SplitByCharacters (null, count, removeEmpty);
263 if (Length == 0 && removeEmpty)
264 return EmptyArray<string>.Value;
266 List<String> arr = new List<String> ();
270 while (pos < this.Length) {
272 int matchPos = Int32.MaxValue;
274 // Find the first position where any of the separators matches
275 for (int i = 0; i < separator.Length; ++i) {
276 string sep = separator [i];
277 if (sep == null || sep.Length == 0)
280 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
281 if (match >= 0 && match < matchPos) {
287 if (matchIndex == -1)
290 if (!(matchPos == pos && removeEmpty)) {
291 if (arr.Count == count - 1)
293 arr.Add (this.Substring (pos, matchPos - pos));
296 pos = matchPos + separator [matchIndex].Length;
302 return new String [] { this };
304 // string contained only separators
305 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
306 return EmptyArray<string>.Value;
308 if (!(removeEmpty && pos == this.Length))
309 arr.Add (this.Substring (pos));
311 return arr.ToArray ();
314 // .NET 2.0 compatibility only
315 #if !NET_4_0 && !MOBILE
316 static readonly char[] WhiteChars = {
317 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
318 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
319 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
320 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
321 (char) 0x3000, (char) 0xFEFF
325 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
327 #if !NET_4_0 && !MOBILE
328 if (sep == null || sep.Length == 0)
332 int[] split_points = null;
333 int total_points = 0;
336 if (sep == null || sep.Length == 0) {
337 fixed (char* src = this) {
342 if (char.IsWhiteSpace (*src_ptr++)) {
343 if (split_points == null) {
344 split_points = new int[8];
345 } else if (split_points.Length == total_points) {
346 Array.Resize (ref split_points, split_points.Length * 2);
349 split_points[total_points++] = Length - len;
350 if (total_points == count && !removeEmpty)
357 fixed (char* src = this) {
358 fixed (char* sep_src = sep) {
360 char* sep_ptr_end = sep_src + sep.Length;
363 char* sep_ptr = sep_src;
365 if (*sep_ptr++ == *src_ptr) {
366 if (split_points == null) {
367 split_points = new int[8];
368 } else if (split_points.Length == total_points) {
369 Array.Resize (ref split_points, split_points.Length * 2);
372 split_points[total_points++] = Length - len;
373 if (total_points == count && !removeEmpty)
378 } while (sep_ptr != sep_ptr_end);
387 if (total_points == 0)
388 return new string[] { this };
390 var res = new string[Math.Min (total_points, count) + 1];
394 for (; i < total_points; ++i) {
395 var start = split_points[i];
396 res[i] = SubstringUnchecked (prev_index, start - prev_index);
397 prev_index = start + 1;
400 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
404 for (; i < total_points; ++i) {
405 var start = split_points[i];
406 length = start - prev_index;
411 res[used++] = SubstringUnchecked (prev_index, length);
414 prev_index = start + 1;
417 length = Length - prev_index;
419 res[used++] = SubstringUnchecked (prev_index, length);
421 if (used != res.Length)
422 Array.Resize (ref res, used);
428 public String Substring (int startIndex)
432 if (startIndex < 0 || startIndex > this.length)
433 throw new ArgumentOutOfRangeException ("startIndex");
435 return SubstringUnchecked (startIndex, this.length - startIndex);
438 public String Substring (int startIndex, int length)
441 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
443 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
444 if (startIndex > this.length)
445 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
446 if (startIndex > this.length - length)
447 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
448 if (startIndex == 0 && length == this.length)
451 return SubstringUnchecked (startIndex, length);
454 // This method is used by StringBuilder.ToString() and is expected to
455 // always create a new string object (or return String.Empty).
456 internal unsafe String SubstringUnchecked (int startIndex, int length)
461 string tmp = InternalAllocateStr (length);
462 fixed (char* dest = tmp, src = this) {
463 CharCopy (dest, src + startIndex, length);
468 public String Trim ()
472 int start = FindNotWhiteSpace (0, length, 1);
477 int end = FindNotWhiteSpace (length - 1, start, -1);
479 int newLength = end - start + 1;
480 if (newLength == length)
483 return SubstringUnchecked (start, newLength);
486 public String Trim (params char[] trimChars)
488 if (trimChars == null || trimChars.Length == 0)
493 int start = FindNotInTable (0, length, 1, trimChars);
498 int end = FindNotInTable (length - 1, start, -1, trimChars);
500 int newLength = end - start + 1;
501 if (newLength == length)
504 return SubstringUnchecked (start, newLength);
507 public String TrimStart (params char[] trimChars)
512 if (trimChars == null || trimChars.Length == 0)
513 start = FindNotWhiteSpace (0, length, 1);
515 start = FindNotInTable (0, length, 1, trimChars);
520 return SubstringUnchecked (start, length - start);
523 public String TrimEnd (params char[] trimChars)
528 if (trimChars == null || trimChars.Length == 0)
529 end = FindNotWhiteSpace (length - 1, -1, -1);
531 end = FindNotInTable (length - 1, -1, -1, trimChars);
537 return SubstringUnchecked (0, end);
540 unsafe int FindNotWhiteSpace (int pos, int target, int change)
543 fixed (char* src = this) {
544 while (pos != target) {
545 if (!char.IsWhiteSpace (src[pos]))
552 while (pos != target) {
556 if (c < 0x9 || c > 0xD)
561 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
562 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
563 if (c < 0x2000 || c > 0x200B)
573 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
575 fixed (char* tablePtr = table, thisPtr = this) {
576 while (pos != target) {
577 char c = thisPtr[pos];
579 while (x < table.Length) {
580 if (c == tablePtr[x])
584 if (x == table.Length)
592 public static int Compare (String strA, String strB)
594 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
597 public static int Compare (String strA, String strB, bool ignoreCase)
599 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
602 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
605 throw new ArgumentNullException ("culture");
607 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
610 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
612 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
615 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
617 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
620 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
623 throw new ArgumentNullException ("culture");
625 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
626 throw new ArgumentOutOfRangeException ();
638 else if (strB == null) {
642 CompareOptions compopts;
645 compopts = CompareOptions.IgnoreCase;
647 compopts = CompareOptions.None;
649 // Need to cap the requested length to the
650 // length of the string, because
651 // CompareInfo.Compare will insist that length
652 // <= (string.Length - offset)
657 if (length > (strA.Length - indexA)) {
658 len1 = strA.Length - indexA;
661 if (length > (strB.Length - indexB)) {
662 len2 = strB.Length - indexB;
665 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
666 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
669 public static int Compare (string strA, string strB, StringComparison comparisonType)
671 switch (comparisonType) {
672 case StringComparison.CurrentCulture:
673 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
674 case StringComparison.CurrentCultureIgnoreCase:
675 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
676 case StringComparison.InvariantCulture:
677 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
678 case StringComparison.InvariantCultureIgnoreCase:
679 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
680 case StringComparison.Ordinal:
681 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
682 case StringComparison.OrdinalIgnoreCase:
683 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
685 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
686 throw new ArgumentException (msg, "comparisonType");
690 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
692 switch (comparisonType) {
693 case StringComparison.CurrentCulture:
694 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
695 case StringComparison.CurrentCultureIgnoreCase:
696 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
697 case StringComparison.InvariantCulture:
698 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
699 case StringComparison.InvariantCultureIgnoreCase:
700 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
701 case StringComparison.Ordinal:
702 return CompareOrdinal (strA, indexA, strB, indexB, length);
703 case StringComparison.OrdinalIgnoreCase:
704 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
706 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
707 throw new ArgumentException (msg, "comparisonType");
711 public static bool Equals (string a, string b, StringComparison comparisonType)
713 return Compare (a, b, comparisonType) == 0;
716 public bool Equals (string value, StringComparison comparisonType)
718 return Compare (value, this, comparisonType) == 0;
721 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
724 throw new ArgumentNullException ("culture");
726 return culture.CompareInfo.Compare (strA, strB, options);
729 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
732 throw new ArgumentNullException ("culture");
737 if (length > (strA.Length - indexA))
738 len1 = strA.Length - indexA;
740 if (length > (strB.Length - indexB))
741 len2 = strB.Length - indexB;
743 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
746 public int CompareTo (Object value)
751 if (!(value is String))
752 throw new ArgumentException ();
754 return String.Compare (this, (String) value);
757 public int CompareTo (String strB)
762 return Compare (this, strB);
765 public static int CompareOrdinal (String strA, String strB)
767 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
770 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
772 if (strA != null && strB != null)
774 if (indexA > strA.Length || indexA < 0)
775 throw new ArgumentOutOfRangeException ("indexA");
776 if (indexB > strB.Length || indexB < 0)
777 throw new ArgumentOutOfRangeException ("indexB");
779 throw new ArgumentOutOfRangeException ("length");
782 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
785 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
787 if (strA != null && strB != null)
789 if (indexA > strA.Length || indexA < 0)
790 throw new ArgumentOutOfRangeException ("indexA");
791 if (indexB > strB.Length || indexB < 0)
792 throw new ArgumentOutOfRangeException ("indexB");
794 throw new ArgumentOutOfRangeException ("length");
797 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
800 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
803 return strB == null ? 0 : -1;
808 int lengthA = Math.Min (lenA, strA.Length - indexA);
809 int lengthB = Math.Min (lenB, strB.Length - indexB);
811 if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
814 fixed (char* aptr = strA, bptr = strB) {
815 char* ap = aptr + indexA;
816 char* end = ap + Math.Min (lengthA, lengthB);
817 char* bp = bptr + indexB;
824 return lengthA - lengthB;
829 // Fastest method for internal case insensitive comparison
831 internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
833 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
836 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
838 // Same as above, but checks versus uppercase characters
840 return strB == null ? 0 : -1;
845 int lengthA = Math.Min (lenA, strA.Length - indexA);
846 int lengthB = Math.Min (lenB, strB.Length - indexB);
848 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
851 fixed (char* aptr = strA, bptr = strB) {
852 char* ap = aptr + indexA;
853 char* end = ap + Math.Min (lengthA, lengthB);
854 char* bp = bptr + indexB;
857 char c1 = Char.ToUpperInvariant (*ap);
858 char c2 = Char.ToUpperInvariant (*bp);
865 return lengthA - lengthB;
869 public bool EndsWith (String value)
872 throw new ArgumentNullException ("value");
874 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
877 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
880 throw new ArgumentNullException ("value");
882 culture = CultureInfo.CurrentCulture;
884 return culture.CompareInfo.IsSuffix (this, value,
885 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
888 // Following methods are culture-insensitive
889 public int IndexOfAny (char [] anyOf)
892 throw new ArgumentNullException ();
893 if (this.length == 0)
896 return IndexOfAnyUnchecked (anyOf, 0, this.length);
899 public int IndexOfAny (char [] anyOf, int startIndex)
902 throw new ArgumentNullException ();
903 if (startIndex < 0 || startIndex > this.length)
904 throw new ArgumentOutOfRangeException ();
906 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
909 public int IndexOfAny (char [] anyOf, int startIndex, int count)
912 throw new ArgumentNullException ();
913 if (startIndex < 0 || startIndex > this.length)
914 throw new ArgumentOutOfRangeException ();
915 if (count < 0 || startIndex > this.length - count)
916 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
918 return IndexOfAnyUnchecked (anyOf, startIndex, count);
921 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
923 if (anyOf.Length == 0)
926 if (anyOf.Length == 1)
927 return IndexOfUnchecked (anyOf[0], startIndex, count);
929 fixed (char* any = anyOf) {
933 char* end_any_ptr = any + anyOf.Length;
935 while (++any_ptr != end_any_ptr) {
936 if (*any_ptr > highest) {
941 if (*any_ptr < lowest)
945 fixed (char* start = &start_char) {
946 char* ptr = start + startIndex;
947 char* end_ptr = ptr + count;
949 while (ptr != end_ptr) {
950 if (*ptr > highest || *ptr < lowest) {
956 return (int)(ptr - start);
959 while (++any_ptr != end_any_ptr) {
960 if (*ptr == *any_ptr)
961 return (int)(ptr - start);
972 public int IndexOf (string value, StringComparison comparisonType)
974 return IndexOf (value, 0, this.Length, comparisonType);
977 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
979 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
982 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
984 switch (comparisonType) {
985 case StringComparison.CurrentCulture:
986 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
987 case StringComparison.CurrentCultureIgnoreCase:
988 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
989 case StringComparison.InvariantCulture:
990 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
991 case StringComparison.InvariantCultureIgnoreCase:
992 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
993 case StringComparison.Ordinal:
994 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
995 case StringComparison.OrdinalIgnoreCase:
996 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
998 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
999 throw new ArgumentException (msg, "comparisonType");
1003 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1006 throw new ArgumentNullException ("value");
1008 throw new ArgumentOutOfRangeException ("startIndex");
1009 if (count < 0 || (this.length - startIndex) < count)
1010 throw new ArgumentOutOfRangeException ("count");
1012 if (options == CompareOptions.Ordinal)
1013 return IndexOfOrdinalUnchecked (value, startIndex, count);
1014 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1017 internal unsafe int IndexOfOrdinalUnchecked (string value)
1019 return IndexOfOrdinalUnchecked (value, 0, length);
1022 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
1024 int valueLen = value.Length;
1025 if (count < valueLen)
1028 if (valueLen <= 1) {
1030 return IndexOfUnchecked (value[0], startIndex, count);
1034 fixed (char* thisptr = this, valueptr = value) {
1035 char* ap = thisptr + startIndex;
1036 char* thisEnd = ap + count - valueLen + 1;
1037 while (ap != thisEnd) {
1038 if (*ap == *valueptr) {
1039 for (int i = 1; i < valueLen; i++) {
1040 if (ap[i] != valueptr[i])
1043 return (int)(ap - thisptr);
1052 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1054 int valueLen = value.Length;
1055 if (count < valueLen)
1061 fixed (char* thisptr = this, valueptr = value) {
1062 char* ap = thisptr + startIndex;
1063 char* thisEnd = ap + count - valueLen + 1;
1064 while (ap != thisEnd) {
1065 for (int i = 0; i < valueLen; i++) {
1066 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1069 return (int)(ap - thisptr);
1077 public int LastIndexOf (string value, StringComparison comparisonType)
1079 if (this.Length == 0)
1080 return value.Length == 0 ? 0 : -1;
1082 return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
1085 public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
1087 return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
1090 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
1092 switch (comparisonType) {
1093 case StringComparison.CurrentCulture:
1094 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1095 case StringComparison.CurrentCultureIgnoreCase:
1096 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1097 case StringComparison.InvariantCulture:
1098 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
1099 case StringComparison.InvariantCultureIgnoreCase:
1100 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
1101 case StringComparison.Ordinal:
1102 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
1103 case StringComparison.OrdinalIgnoreCase:
1104 return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
1106 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1107 throw new ArgumentException (msg, "comparisonType");
1111 internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
1114 throw new ArgumentNullException ("value");
1115 if (this.Length == 0)
1116 return value.Length == 0 ? 0 : -1;
1117 if (value.Length == 0)
1118 return Math.Min (this.Length - 1, startIndex);
1119 if (startIndex < 0 || startIndex > length)
1120 throw new ArgumentOutOfRangeException ("startIndex");
1121 if (count < 0 || (startIndex < count - 1))
1122 throw new ArgumentOutOfRangeException ("count");
1124 if (options == CompareOptions.Ordinal)
1125 return LastIndexOfOrdinalUnchecked (value, startIndex, count);
1126 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
1129 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
1131 int valueLen = value.Length;
1132 if (count < valueLen)
1135 if (valueLen <= 1) {
1137 return LastIndexOfUnchecked (value[0], startIndex, count);
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 if (*ap == *valueptr) {
1146 for (int i = 1; i < valueLen; i++) {
1147 if (ap[i] != valueptr[i])
1150 return (int)(ap - thisptr);
1159 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
1161 int valueLen = value.Length;
1162 if (count < valueLen)
1168 fixed (char* thisptr = this, valueptr = value) {
1169 char* ap = thisptr + startIndex - valueLen + 1;
1170 char* thisEnd = ap - count + valueLen - 1;
1171 while (ap != thisEnd) {
1172 for (int i = 0; i < valueLen; i++) {
1173 if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
1176 return (int)(ap - thisptr);
1184 // Following methods are culture-insensitive
1185 public int IndexOf (char value)
1187 if (this.length == 0)
1190 return IndexOfUnchecked (value, 0, this.length);
1193 public int IndexOf (char value, int startIndex)
1196 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1197 if (startIndex > this.length)
1198 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1200 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
1203 return IndexOfUnchecked (value, startIndex, this.length - startIndex);
1206 public int IndexOf (char value, int startIndex, int count)
1208 if (startIndex < 0 || startIndex > this.length)
1209 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1211 throw new ArgumentOutOfRangeException ("count", "< 0");
1212 if (startIndex > this.length - count)
1213 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1215 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
1218 return IndexOfUnchecked (value, startIndex, count);
1221 internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
1223 // It helps JIT compiler to optimize comparison
1224 int value_32 = (int)value;
1226 fixed (char* start = &start_char) {
1227 char* ptr = start + startIndex;
1228 char* end_ptr = ptr + (count >> 3 << 3);
1230 while (ptr != end_ptr) {
1231 if (*ptr == value_32)
1232 return (int)(ptr - start);
1233 if (ptr[1] == value_32)
1234 return (int)(ptr - start + 1);
1235 if (ptr[2] == value_32)
1236 return (int)(ptr - start + 2);
1237 if (ptr[3] == value_32)
1238 return (int)(ptr - start + 3);
1239 if (ptr[4] == value_32)
1240 return (int)(ptr - start + 4);
1241 if (ptr[5] == value_32)
1242 return (int)(ptr - start + 5);
1243 if (ptr[6] == value_32)
1244 return (int)(ptr - start + 6);
1245 if (ptr[7] == value_32)
1246 return (int)(ptr - start + 7);
1251 end_ptr += count & 0x07;
1252 while (ptr != end_ptr) {
1253 if (*ptr == value_32)
1254 return (int)(ptr - start);
1262 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1266 int end = startIndex + count;
1267 char c = Char.ToUpperInvariant (value);
1268 fixed (char* s = &start_char) {
1269 for (int i = startIndex; i < end; i++)
1270 if (Char.ToUpperInvariant (s [i]) == c)
1276 // Following methods are culture-sensitive
1277 public int IndexOf (String value)
1280 throw new ArgumentNullException ("value");
1281 if (value.Length == 0)
1283 if (this.length == 0)
1285 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.None);
1288 public int IndexOf (String value, int startIndex)
1290 return IndexOf (value, startIndex, this.length - startIndex);
1293 public int IndexOf (String value, int startIndex, int count)
1296 throw new ArgumentNullException ("value");
1297 if (startIndex < 0 || startIndex > this.length)
1298 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1299 if (count < 0 || startIndex > this.length - count)
1300 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1302 if (value.length == 0)
1305 if (startIndex == 0 && this.length == 0)
1311 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
1314 // Following methods are culture-insensitive
1315 public int LastIndexOfAny (char [] anyOf)
1318 throw new ArgumentNullException ();
1319 if (this.length == 0)
1322 return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
1325 public int LastIndexOfAny (char [] anyOf, int startIndex)
1328 throw new ArgumentNullException ();
1329 if (this.length == 0)
1332 if (startIndex < 0 || startIndex >= this.length)
1333 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1335 if (this.length == 0)
1338 return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
1341 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
1344 throw new ArgumentNullException ();
1345 if (this.length == 0)
1348 if ((startIndex < 0) || (startIndex >= this.Length))
1349 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1350 if ((count < 0) || (count > this.Length))
1351 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1352 if (startIndex - count + 1 < 0)
1353 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1355 if (this.length == 0)
1358 return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
1361 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
1363 if (anyOf.Length == 1)
1364 return LastIndexOfUnchecked (anyOf[0], startIndex, count);
1366 fixed (char* start = this, testStart = anyOf) {
1367 char* ptr = start + startIndex;
1368 char* ptrEnd = ptr - count;
1370 char* testEnd = testStart + anyOf.Length;
1372 while (ptr != ptrEnd) {
1374 while (test != testEnd) {
1376 return (int)(ptr - start);
1385 // Following methods are culture-insensitive
1386 public int LastIndexOf (char value)
1388 if (this.length == 0)
1391 return LastIndexOfUnchecked (value, this.length - 1, this.length);
1394 public int LastIndexOf (char value, int startIndex)
1396 return LastIndexOf (value, startIndex, startIndex + 1);
1399 public int LastIndexOf (char value, int startIndex, int count)
1401 if (this.length == 0)
1404 // >= for char (> for string)
1405 if ((startIndex < 0) || (startIndex >= this.Length))
1406 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1407 if ((count < 0) || (count > this.Length))
1408 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1409 if (startIndex - count + 1 < 0)
1410 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1412 return LastIndexOfUnchecked (value, startIndex, count);
1415 internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
1417 // It helps JIT compiler to optimize comparison
1418 int value_32 = (int)value;
1420 fixed (char* start = &start_char) {
1421 char* ptr = start + startIndex;
1422 char* end_ptr = ptr - (count >> 3 << 3);
1424 while (ptr != end_ptr) {
1425 if (*ptr == value_32)
1426 return (int)(ptr - start);
1427 if (ptr[-1] == value_32)
1428 return (int)(ptr - start) - 1;
1429 if (ptr[-2] == value_32)
1430 return (int)(ptr - start) - 2;
1431 if (ptr[-3] == value_32)
1432 return (int)(ptr - start) - 3;
1433 if (ptr[-4] == value_32)
1434 return (int)(ptr - start) - 4;
1435 if (ptr[-5] == value_32)
1436 return (int)(ptr - start) - 5;
1437 if (ptr[-6] == value_32)
1438 return (int)(ptr - start) - 6;
1439 if (ptr[-7] == value_32)
1440 return (int)(ptr - start) - 7;
1445 end_ptr -= count & 0x07;
1446 while (ptr != end_ptr) {
1447 if (*ptr == value_32)
1448 return (int)(ptr - start);
1456 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
1460 int end = startIndex - count;
1461 char c = Char.ToUpperInvariant (value);
1462 fixed (char* s = &start_char) {
1463 for (int i = startIndex; i > end; i--)
1464 if (Char.ToUpperInvariant (s [i]) == c)
1470 // Following methods are culture-sensitive
1471 public int LastIndexOf (String value)
1473 return LastIndexOf (value, this.length - 1, this.length);
1476 public int LastIndexOf (String value, int startIndex)
1478 int max = startIndex;
1479 if (max < this.Length)
1481 return LastIndexOf (value, startIndex, max);
1484 public int LastIndexOf (String value, int startIndex, int count)
1487 throw new ArgumentNullException ("value");
1489 if (this.length == 0)
1490 return value.Length == 0 ? 0 : -1;
1491 // -1 > startIndex > for string (0 > startIndex >= for char)
1492 if ((startIndex < -1) || (startIndex > this.Length))
1493 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1494 if ((count < 0) || (count > this.Length))
1495 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1496 if (startIndex - count + 1 < 0)
1497 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1499 if (value.Length == 0)
1500 return Math.Min (this.Length - 1, startIndex);
1502 if (startIndex == 0 && this.length == 0)
1505 // This check is needed to match undocumented MS behaviour
1506 if (this.length == 0 && value.length > 0)
1512 if (startIndex == this.Length)
1514 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
1517 public bool Contains (String value)
1520 throw new ArgumentNullException ("value");
1522 return IndexOfOrdinalUnchecked (value, 0, Length) != -1;
1525 public static bool IsNullOrEmpty (String value)
1527 return (value == null) || (value.Length == 0);
1530 public string Normalize ()
1532 return Normalization.Normalize (this, 0);
1535 public string Normalize (NormalizationForm normalizationForm)
1537 switch (normalizationForm) {
1539 return Normalization.Normalize (this, 0);
1540 case NormalizationForm.FormD:
1541 return Normalization.Normalize (this, 1);
1542 case NormalizationForm.FormKC:
1543 return Normalization.Normalize (this, 2);
1544 case NormalizationForm.FormKD:
1545 return Normalization.Normalize (this, 3);
1549 public bool IsNormalized ()
1551 return Normalization.IsNormalized (this, 0);
1554 public bool IsNormalized (NormalizationForm normalizationForm)
1556 switch (normalizationForm) {
1558 return Normalization.IsNormalized (this, 0);
1559 case NormalizationForm.FormD:
1560 return Normalization.IsNormalized (this, 1);
1561 case NormalizationForm.FormKC:
1562 return Normalization.IsNormalized (this, 2);
1563 case NormalizationForm.FormKD:
1564 return Normalization.IsNormalized (this, 3);
1568 public string Remove (int startIndex)
1571 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1572 if (startIndex >= this.length)
1573 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1575 return Remove (startIndex, this.length - startIndex);
1578 public String PadLeft (int totalWidth)
1580 return PadLeft (totalWidth, ' ');
1583 public unsafe String PadLeft (int totalWidth, char paddingChar)
1585 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1588 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1590 if (totalWidth < this.length)
1592 if (totalWidth == 0)
1595 String tmp = InternalAllocateStr (totalWidth);
1597 fixed (char* dest = tmp, src = this) {
1598 char* padPos = dest;
1599 char* padTo = dest + (totalWidth - length);
1600 while (padPos != padTo)
1601 *padPos++ = paddingChar;
1603 CharCopy (padTo, src, length);
1608 public String PadRight (int totalWidth)
1610 return PadRight (totalWidth, ' ');
1613 public unsafe String PadRight (int totalWidth, char paddingChar)
1615 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1618 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1620 if (totalWidth < this.length)
1622 if (totalWidth == 0)
1625 String tmp = InternalAllocateStr (totalWidth);
1627 fixed (char* dest = tmp, src = this) {
1628 CharCopy (dest, src, length);
1630 char* padPos = dest + length;
1631 char* padTo = dest + totalWidth;
1632 while (padPos != padTo)
1633 *padPos++ = paddingChar;
1638 public bool StartsWith (String value)
1641 throw new ArgumentNullException ("value");
1643 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1646 [ComVisible (false)]
1647 public bool StartsWith (string value, StringComparison comparisonType)
1650 throw new ArgumentNullException ("value");
1652 switch (comparisonType) {
1653 case StringComparison.CurrentCulture:
1654 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1655 case StringComparison.CurrentCultureIgnoreCase:
1656 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1657 case StringComparison.InvariantCulture:
1658 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1659 case StringComparison.InvariantCultureIgnoreCase:
1660 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1661 case StringComparison.Ordinal:
1662 return StartsWithOrdinalUnchecked (value);
1663 case StringComparison.OrdinalIgnoreCase:
1664 return StartsWithOrdinalCaseInsensitiveUnchecked (value);
1666 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1667 throw new ArgumentException (msg, "comparisonType");
1671 internal bool StartsWithOrdinalUnchecked (string value)
1673 return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1676 internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
1678 return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
1681 [ComVisible (false)]
1682 public bool EndsWith (string value, StringComparison comparisonType)
1685 throw new ArgumentNullException ("value");
1687 switch (comparisonType) {
1688 case StringComparison.CurrentCulture:
1689 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1690 case StringComparison.CurrentCultureIgnoreCase:
1691 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1692 case StringComparison.InvariantCulture:
1693 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1694 case StringComparison.InvariantCultureIgnoreCase:
1695 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1696 case StringComparison.Ordinal:
1697 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1698 case StringComparison.OrdinalIgnoreCase:
1699 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1701 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1702 throw new ArgumentException (msg, "comparisonType");
1706 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1708 if (culture == null)
1709 culture = CultureInfo.CurrentCulture;
1711 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1714 // Following method is culture-insensitive
1715 public unsafe String Replace (char oldChar, char newChar)
1717 if (this.length == 0 || oldChar == newChar)
1720 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1721 if (start_pos == -1)
1727 string tmp = InternalAllocateStr (length);
1728 fixed (char* dest = tmp, src = &start_char) {
1730 CharCopy (dest, src, start_pos);
1732 char* end_ptr = dest + length;
1733 char* dest_ptr = dest + start_pos;
1734 char* src_ptr = src + start_pos;
1736 while (dest_ptr != end_ptr) {
1737 if (*src_ptr == oldChar)
1738 *dest_ptr = newChar;
1740 *dest_ptr = *src_ptr;
1749 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1750 public String Replace (String oldValue, String newValue)
1752 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1753 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1755 if (oldValue == null)
1756 throw new ArgumentNullException ("oldValue");
1758 if (oldValue.Length == 0)
1759 throw new ArgumentException ("oldValue is the empty string.");
1761 if (this.Length == 0)
1764 if (newValue == null)
1767 return ReplaceUnchecked (oldValue, newValue);
1770 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1772 if (oldValue.length > length)
1774 if (oldValue.length == 1 && newValue.length == 1) {
1775 return Replace (oldValue[0], newValue[0]);
1776 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1777 // because the length of the result would be this.length and length calculation unneccesary
1780 const int maxValue = 200; // Allocate 800 byte maximum
1781 int* dat = stackalloc int[maxValue];
1782 fixed (char* source = this, replace = newValue) {
1783 int i = 0, count = 0;
1784 while (i < length) {
1785 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1789 if (count < maxValue)
1790 dat[count++] = found;
1792 return ReplaceFallback (oldValue, newValue, maxValue);
1794 i = found + oldValue.length;
1798 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1799 String tmp = InternalAllocateStr (nlen);
1801 int curPos = 0, lastReadPos = 0;
1802 fixed (char* dest = tmp) {
1803 for (int j = 0; j < count; j++) {
1804 int precopy = dat[j] - lastReadPos;
1805 CharCopy (dest + curPos, source + lastReadPos, precopy);
1807 lastReadPos = dat[j] + oldValue.length;
1808 CharCopy (dest + curPos, replace, newValue.length);
1809 curPos += newValue.length;
1811 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1817 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1819 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1820 StringBuilder sb = new StringBuilder (lengthEstimate);
1821 for (int i = 0; i < length;) {
1822 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1824 sb.Append (SubstringUnchecked (i, length - i));
1827 sb.Append (SubstringUnchecked (i, found - i));
1828 sb.Append (newValue);
1829 i = found + oldValue.Length;
1831 return sb.ToString ();
1835 public unsafe String Remove (int startIndex, int count)
1838 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1840 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1841 if (startIndex > this.length - count)
1842 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1844 String tmp = InternalAllocateStr (this.length - count);
1846 fixed (char *dest = tmp, src = this) {
1848 CharCopy (dst, src, startIndex);
1849 int skip = startIndex + count;
1851 CharCopy (dst, src + skip, length - skip);
1856 public String ToLower ()
1858 return ToLower (CultureInfo.CurrentCulture);
1861 public String ToLower (CultureInfo culture)
1863 if (culture == null)
1864 throw new ArgumentNullException ("culture");
1866 if (culture.LCID == 0x007F) // Invariant
1867 return ToLowerInvariant ();
1869 return culture.TextInfo.ToLower (this);
1872 public unsafe String ToLowerInvariant ()
1877 string tmp = InternalAllocateStr (length);
1878 fixed (char* source = &start_char, dest = tmp) {
1880 char* destPtr = (char*)dest;
1881 char* sourcePtr = (char*)source;
1883 for (int n = 0; n < length; n++) {
1884 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1892 public String ToUpper ()
1894 return ToUpper (CultureInfo.CurrentCulture);
1897 public String ToUpper (CultureInfo culture)
1899 if (culture == null)
1900 throw new ArgumentNullException ("culture");
1902 if (culture.LCID == 0x007F) // Invariant
1903 return ToUpperInvariant ();
1905 return culture.TextInfo.ToUpper (this);
1908 public unsafe String ToUpperInvariant ()
1913 string tmp = InternalAllocateStr (length);
1914 fixed (char* source = &start_char, dest = tmp) {
1916 char* destPtr = (char*)dest;
1917 char* sourcePtr = (char*)source;
1919 for (int n = 0; n < length; n++) {
1920 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1928 public override String ToString ()
1933 public String ToString (IFormatProvider provider)
1938 public static String Format (String format, Object arg0)
1940 return Format (null, format, new Object[] {arg0});
1943 public static String Format (String format, Object arg0, Object arg1)
1945 return Format (null, format, new Object[] {arg0, arg1});
1948 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1950 return Format (null, format, new Object[] {arg0, arg1, arg2});
1953 public static string Format (string format, params object[] args)
1955 return Format (null, format, args);
1958 public static string Format (IFormatProvider provider, string format, params object[] args)
1960 StringBuilder b = FormatHelper (null, provider, format, args);
1961 return b.ToString ();
1964 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1967 throw new ArgumentNullException ("format");
1969 throw new ArgumentNullException ("args");
1971 if (result == null) {
1972 /* Try to approximate the size of result to avoid reallocations */
1976 for (i = 0; i < args.Length; ++i) {
1977 string s = args [i] as string;
1983 if (i == args.Length)
1984 result = new StringBuilder (len + format.length);
1986 result = new StringBuilder ();
1991 var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
1993 while (ptr < format.length) {
1994 char c = format[ptr ++];
1997 result.Append (format, start, ptr - start - 1);
1999 // check for escaped open bracket
2001 if (format[ptr] == '{') {
2012 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
2013 if (n >= args.Length)
2014 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
2018 object arg = args[n];
2023 else if (formatter != null)
2024 str = formatter.Format (arg_format, arg, provider);
2029 if (arg is IFormattable)
2030 str = ((IFormattable)arg).ToString (arg_format, provider);
2032 str = arg.ToString ();
2035 // pad formatted string and append to result
2036 if (width > str.length) {
2037 const char padchar = ' ';
2038 int padlen = width - str.length;
2041 result.Append (str);
2042 result.Append (padchar, padlen);
2045 result.Append (padchar, padlen);
2046 result.Append (str);
2049 result.Append (str);
2054 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2055 result.Append (format, start, ptr - start - 1);
2058 else if (c == '}') {
2059 throw new FormatException ("Input string was not in a correct format.");
2063 if (start < format.length)
2064 result.Append (format, start, format.Length - start);
2069 public unsafe static String Copy (String str)
2072 throw new ArgumentNullException ("str");
2074 int length = str.length;
2076 String tmp = InternalAllocateStr (length);
2078 fixed (char *dest = tmp, src = str) {
2079 CharCopy (dest, src, length);
2085 public static String Concat (Object arg0)
2090 return arg0.ToString ();
2093 public static String Concat (Object arg0, Object arg1)
2095 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2098 public static String Concat (Object arg0, Object arg1, Object arg2)
2104 s1 = arg0.ToString ();
2109 s2 = arg1.ToString ();
2114 s3 = arg2.ToString ();
2116 return Concat (s1, s2, s3);
2119 [CLSCompliant(false)]
2120 public static String Concat (Object arg0, Object arg1, Object arg2,
2121 Object arg3, __arglist)
2123 string s1, s2, s3, s4;
2128 s1 = arg0.ToString ();
2133 s2 = arg1.ToString ();
2138 s3 = arg2.ToString ();
2140 ArgIterator iter = new ArgIterator (__arglist);
2141 int argCount = iter.GetRemainingCount();
2143 StringBuilder sb = new StringBuilder ();
2145 sb.Append (arg3.ToString ());
2147 for (int i = 0; i < argCount; i++) {
2148 TypedReference typedRef = iter.GetNextArg ();
2149 sb.Append (TypedReference.ToObject (typedRef));
2152 s4 = sb.ToString ();
2154 return Concat (s1, s2, s3, s4);
2157 public unsafe static String Concat (String str0, String str1)
2159 if (str0 == null || str0.Length == 0) {
2160 if (str1 == null || str1.Length == 0)
2165 if (str1 == null || str1.Length == 0)
2168 String tmp = InternalAllocateStr (str0.length + str1.length);
2170 fixed (char *dest = tmp, src = str0)
2171 CharCopy (dest, src, str0.length);
2172 fixed (char *dest = tmp, src = str1)
2173 CharCopy (dest + str0.Length, src, str1.length);
2178 public unsafe static String Concat (String str0, String str1, String str2)
2180 if (str0 == null || str0.Length == 0){
2181 if (str1 == null || str1.Length == 0){
2182 if (str2 == null || str2.Length == 0)
2186 if (str2 == null || str2.Length == 0)
2191 if (str1 == null || str1.Length == 0){
2192 if (str2 == null || str2.Length == 0)
2197 if (str2 == null || str2.Length == 0)
2202 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
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 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2239 if (str0.Length != 0) {
2240 fixed (char *dest = tmp, src = str0) {
2241 CharCopy (dest, src, str0.length);
2244 if (str1.Length != 0) {
2245 fixed (char *dest = tmp, src = str1) {
2246 CharCopy (dest + str0.Length, src, str1.length);
2249 if (str2.Length != 0) {
2250 fixed (char *dest = tmp, src = str2) {
2251 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2254 if (str3.Length != 0) {
2255 fixed (char *dest = tmp, src = str3) {
2256 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2263 public static String Concat (params Object[] args)
2266 throw new ArgumentNullException ("args");
2268 int argLen = args.Length;
2272 string [] strings = new string [argLen];
2274 for (int i = 0; i < argLen; i++) {
2275 if (args[i] != null) {
2276 strings[i] = args[i].ToString ();
2277 len += strings[i].length;
2281 return ConcatInternal (strings, len);
2284 public static String Concat (params String[] values)
2287 throw new ArgumentNullException ("values");
2290 for (int i = 0; i < values.Length; i++) {
2291 String s = values[i];
2296 return ConcatInternal (values, len);
2299 private static unsafe String ConcatInternal (String[] values, int length)
2304 String tmp = InternalAllocateStr (length);
2306 fixed (char* dest = tmp) {
2308 for (int i = 0; i < values.Length; i++) {
2309 String source = values[i];
2310 if (source != null) {
2311 fixed (char* src = source) {
2312 CharCopy (dest + pos, src, source.length);
2314 pos += source.Length;
2321 public unsafe String Insert (int startIndex, String value)
2324 throw new ArgumentNullException ("value");
2326 if (startIndex < 0 || startIndex > this.length)
2327 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2329 if (value.Length == 0)
2331 if (this.Length == 0)
2333 String tmp = InternalAllocateStr (this.length + value.length);
2335 fixed (char *dest = tmp, src = this, val = value) {
2337 CharCopy (dst, src, startIndex);
2339 CharCopy (dst, val, value.length);
2340 dst += value.length;
2341 CharCopy (dst, src + startIndex, length - startIndex);
2346 public static string Intern (string str)
2349 throw new ArgumentNullException ("str");
2351 return InternalIntern (str);
2354 public static string IsInterned (string str)
2357 throw new ArgumentNullException ("str");
2359 return InternalIsInterned (str);
2363 public static string Join (string separator, params string [] value)
2365 public static string Join (string separator, string [] value)
2369 throw new ArgumentNullException ("value");
2370 if (separator == null)
2373 return JoinUnchecked (separator, value, 0, value.Length);
2376 public static string Join (string separator, string[] value, int startIndex, int count)
2379 throw new ArgumentNullException ("value");
2381 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2383 throw new ArgumentOutOfRangeException ("count", "< 0");
2384 if (startIndex > value.Length - count)
2385 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2387 if (startIndex == value.Length)
2389 if (separator == null)
2392 return JoinUnchecked (separator, value, startIndex, count);
2395 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2397 // Unchecked parameters
2398 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2399 // separator and value must not be null
2402 int maxIndex = startIndex + count;
2403 // Precount the number of characters that the resulting string will have
2404 for (int i = startIndex; i < maxIndex; i++) {
2405 String s = value[i];
2409 length += separator.length * (count - 1);
2413 String tmp = InternalAllocateStr (length);
2416 fixed (char* dest = tmp, sepsrc = separator) {
2417 // Copy each string from value except the last one and add a separator for each
2419 for (int i = startIndex; i < maxIndex; i++) {
2420 String source = value[i];
2421 if (source != null) {
2422 if (source.Length > 0) {
2423 fixed (char* src = source)
2424 CharCopy (dest + pos, src, source.Length);
2425 pos += source.Length;
2428 if (separator.Length > 0) {
2429 CharCopy (dest + pos, sepsrc, separator.Length);
2430 pos += separator.Length;
2433 // Append last string that does not get an additional separator
2434 String sourceLast = value[maxIndex];
2435 if (sourceLast != null) {
2436 if (sourceLast.Length > 0) {
2437 fixed (char* src = sourceLast)
2438 CharCopy (dest + pos, src, sourceLast.Length);
2445 bool IConvertible.ToBoolean (IFormatProvider provider)
2447 return Convert.ToBoolean (this, provider);
2450 byte IConvertible.ToByte (IFormatProvider provider)
2452 return Convert.ToByte (this, provider);
2455 char IConvertible.ToChar (IFormatProvider provider)
2457 return Convert.ToChar (this, provider);
2460 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2462 return Convert.ToDateTime (this, provider);
2465 decimal IConvertible.ToDecimal (IFormatProvider provider)
2467 return Convert.ToDecimal (this, provider);
2470 double IConvertible.ToDouble (IFormatProvider provider)
2472 return Convert.ToDouble (this, provider);
2475 short IConvertible.ToInt16 (IFormatProvider provider)
2477 return Convert.ToInt16 (this, provider);
2480 int IConvertible.ToInt32 (IFormatProvider provider)
2482 return Convert.ToInt32 (this, provider);
2485 long IConvertible.ToInt64 (IFormatProvider provider)
2487 return Convert.ToInt64 (this, provider);
2490 sbyte IConvertible.ToSByte (IFormatProvider provider)
2492 return Convert.ToSByte (this, provider);
2495 float IConvertible.ToSingle (IFormatProvider provider)
2497 return Convert.ToSingle (this, provider);
2500 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2502 if (targetType == null)
2503 throw new ArgumentNullException ("type");
2504 return Convert.ToType (this, targetType, provider, false);
2507 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2509 return Convert.ToUInt16 (this, provider);
2512 uint IConvertible.ToUInt32 (IFormatProvider provider)
2514 return Convert.ToUInt32 (this, provider);
2517 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2519 return Convert.ToUInt64 (this, provider);
2528 public CharEnumerator GetEnumerator ()
2530 return new CharEnumerator (this);
2533 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2535 return new CharEnumerator (this);
2538 IEnumerator IEnumerable.GetEnumerator ()
2540 return new CharEnumerator (this);
2543 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2544 out bool left_align, out string format)
2546 int max = str.Length;
2548 // parses format specifier of form:
2552 // N = argument number (non-negative integer)
2554 n = ParseDecimal (str, ref ptr);
2556 throw new FormatException ("Input string was not in a correct format.");
2558 // M = width (non-negative integer)
2560 if (ptr < max && str[ptr] == ',') {
2561 // White space between ',' and number or sign.
2563 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2567 format = str.Substring (start, ptr - start);
2569 left_align = (ptr < max && str [ptr] == '-');
2573 width = ParseDecimal (str, ref ptr);
2575 throw new FormatException ("Input string was not in a correct format.");
2583 // F = argument format (string)
2585 if (ptr < max && str[ptr] == ':') {
2587 while (ptr < max && str[ptr] != '}')
2590 format += str.Substring (start, ptr - start);
2595 if ((ptr >= max) || str[ptr ++] != '}')
2596 throw new FormatException ("Input string was not in a correct format.");
2599 private static int ParseDecimal (string str, ref int ptr)
2603 int max = str.Length;
2607 if (c < '0' || '9' < c)
2610 n = n * 10 + c - '0';
2614 if (p == ptr || p == max)
2621 internal unsafe void InternalSetChar (int idx, char val)
2623 if ((uint) idx >= (uint) Length)
2624 throw new ArgumentOutOfRangeException ("idx");
2626 fixed (char * pStr = &start_char)
2632 internal unsafe void InternalSetLength (int newLength)
2634 if (newLength > length)
2635 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2637 // zero terminate, we can pass string objects directly via pinvoke
2638 // we also zero the rest of the string, since the new GC needs to be
2639 // able to handle the changing size (it will skip the 0 bytes).
2640 fixed (char * pStr = &start_char) {
2641 char *p = pStr + newLength;
2642 char *end = pStr + length;
2651 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2652 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2653 public unsafe override int GetHashCode ()
2655 fixed (char * c = this) {
2657 char * end = cc + length - 1;
2659 for (;cc < end; cc += 2) {
2660 h = (h << 5) - h + *cc;
2661 h = (h << 5) - h + cc [1];
2665 h = (h << 5) - h + *cc;
2672 public static string Concat (IEnumerable<string> values)
2675 throw new ArgumentNullException ("values");
2677 var stringList = new List<string> ();
2679 foreach (var v in values){
2685 return ConcatInternal (stringList.ToArray (), len);
2688 [ComVisibleAttribute(false)]
2689 public static string Concat<T> (IEnumerable<T> values)
2692 throw new ArgumentNullException ("values");
2694 var stringList = new List<string> ();
2696 foreach (var v in values){
2697 string sr = v.ToString ();
2699 stringList.Add (sr);
2701 return ConcatInternal (stringList.ToArray (), len);
2704 [ComVisibleAttribute(false)]
2705 public static string Join (string separator, IEnumerable<string> values)
2707 if (separator == null)
2708 return Concat (values);
2711 throw new ArgumentNullException ("values");
2713 var stringList = new List<string> (values);
2715 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2718 [ComVisibleAttribute(false)]
2719 public static string Join (string separator, params object [] values)
2721 if (separator == null)
2722 return Concat (values);
2725 throw new ArgumentNullException ("values");
2727 var strCopy = new string [values.Length];
2729 foreach (var v in values)
2730 strCopy [i++] = v.ToString ();
2732 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2735 [ComVisible (false)]
2736 public static string Join<T> (string separator, IEnumerable<T> values)
2738 if (separator == null)
2739 return Concat<T> (values);
2742 throw new ArgumentNullException ("values");
2744 var stringList = values as IList<T> ?? new List<T> (values);
2745 var strCopy = new string [stringList.Count];
2747 foreach (var v in stringList)
2748 strCopy [i++] = v.ToString ();
2750 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2753 public static bool IsNullOrWhiteSpace (string value)
2755 internal static bool IsNullOrWhiteSpace (string value)
2758 if ((value == null) || (value.Length == 0))
2760 foreach (char c in value)
2761 if (!Char.IsWhiteSpace (c))
2766 internal unsafe int GetCaseInsensitiveHashCode ()
2768 fixed (char * c = this) {
2770 char * end = cc + length - 1;
2772 for (;cc < end; cc += 2) {
2773 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2774 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2778 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2783 // Certain constructors are redirected to CreateString methods with
2784 // matching argument list. The this pointer should not be used.
2786 private unsafe String CreateString (sbyte* value)
2791 byte* bytes = (byte*) value;
2795 while (bytes++ [0] != 0)
2797 } catch (NullReferenceException) {
2798 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2801 return CreateString (value, 0, length, null);
2804 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2806 return CreateString (value, startIndex, length, null);
2809 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2812 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2814 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2815 if (value + startIndex < value)
2816 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2820 throw new ArgumentNullException ("value");
2824 enc = Encoding.Default;
2827 byte [] bytes = new byte [length];
2830 fixed (byte* bytePtr = bytes)
2832 memcpy (bytePtr, (byte*) (value + startIndex), length);
2833 } catch (NullReferenceException) {
2834 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2837 // GetString () is called even when length == 0
2838 return enc.GetString (bytes);
2841 unsafe string CreateString (char *value)
2851 string result = InternalAllocateStr (i);
2854 fixed (char *dest = result) {
2855 CharCopy (dest, value, i);
2861 unsafe string CreateString (char *value, int startIndex, int length)
2866 throw new ArgumentNullException ("value");
2868 throw new ArgumentOutOfRangeException ("startIndex");
2870 throw new ArgumentOutOfRangeException ("length");
2872 string result = InternalAllocateStr (length);
2874 fixed (char *dest = result) {
2875 CharCopy (dest, value + startIndex, length);
2880 unsafe string CreateString (char [] val, int startIndex, int length)
2883 throw new ArgumentNullException ("value");
2885 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2887 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2888 if (startIndex > val.Length - length)
2889 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2893 string result = InternalAllocateStr (length);
2895 fixed (char *dest = result, src = val) {
2896 CharCopy (dest, src + startIndex, length);
2901 unsafe string CreateString (char [] val)
2903 if (val == null || val.Length == 0)
2905 string result = InternalAllocateStr (val.Length);
2907 fixed (char *dest = result, src = val) {
2908 CharCopy (dest, src, val.Length);
2913 unsafe string CreateString (char c, int count)
2916 throw new ArgumentOutOfRangeException ("count");
2919 string result = InternalAllocateStr (count);
2920 fixed (char *dest = result) {
2922 char *end = p + count;
2931 /* helpers used by the runtime as well as above or eslewhere in corlib */
2932 internal static unsafe void memset (byte *dest, int val, int len)
2943 val = val | (val << 8);
2944 val = val | (val << 16);
2947 int rest = (int)dest & 3;
2955 } while (rest != 0);
2958 ((int*)dest) [0] = val;
2959 ((int*)dest) [1] = val;
2960 ((int*)dest) [2] = val;
2961 ((int*)dest) [3] = val;
2966 ((int*)dest) [0] = val;
2978 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2979 /*while (size >= 32) {
2980 // using long is better than int and slower than double
2981 // FIXME: enable this only on correct alignment or on platforms
2982 // that can tolerate unaligned reads/writes of doubles
2983 ((double*)dest) [0] = ((double*)src) [0];
2984 ((double*)dest) [1] = ((double*)src) [1];
2985 ((double*)dest) [2] = ((double*)src) [2];
2986 ((double*)dest) [3] = ((double*)src) [3];
2991 while (size >= 16) {
2992 ((int*)dest) [0] = ((int*)src) [0];
2993 ((int*)dest) [1] = ((int*)src) [1];
2994 ((int*)dest) [2] = ((int*)src) [2];
2995 ((int*)dest) [3] = ((int*)src) [3];
3001 ((int*)dest) [0] = ((int*)src) [0];
3007 ((byte*)dest) [0] = ((byte*)src) [0];
3013 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
3015 ((short*)dest) [0] = ((short*)src) [0];
3016 ((short*)dest) [1] = ((short*)src) [1];
3017 ((short*)dest) [2] = ((short*)src) [2];
3018 ((short*)dest) [3] = ((short*)src) [3];
3024 ((short*)dest) [0] = ((short*)src) [0];
3030 ((byte*)dest) [0] = ((byte*)src) [0];
3032 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3034 ((byte*)dest) [0] = ((byte*)src) [0];
3035 ((byte*)dest) [1] = ((byte*)src) [1];
3036 ((byte*)dest) [2] = ((byte*)src) [2];
3037 ((byte*)dest) [3] = ((byte*)src) [3];
3038 ((byte*)dest) [4] = ((byte*)src) [4];
3039 ((byte*)dest) [5] = ((byte*)src) [5];
3040 ((byte*)dest) [6] = ((byte*)src) [6];
3041 ((byte*)dest) [7] = ((byte*)src) [7];
3047 ((byte*)dest) [0] = ((byte*)src) [0];
3048 ((byte*)dest) [1] = ((byte*)src) [1];
3054 ((byte*)dest) [0] = ((byte*)src) [0];
3057 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3058 // FIXME: if pointers are not aligned, try to align them
3059 // so a faster routine can be used. Handle the case where
3060 // the pointers can't be reduced to have the same alignment
3061 // (just ignore the issue on x86?)
3062 if ((((int)dest | (int)src) & 3) != 0) {
3063 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3069 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3070 ((short*)dest) [0] = ((short*)src) [0];
3075 if ((((int)dest | (int)src) & 1) != 0) {
3076 memcpy1 (dest, src, size);
3079 if ((((int)dest | (int)src) & 2) != 0) {
3080 memcpy2 (dest, src, size);
3084 memcpy4 (dest, src, size);
3087 /* Used by the runtime */
3088 internal static unsafe void bzero (byte *dest, int len) {
3089 memset (dest, 0, len);
3092 internal static unsafe void bzero_aligned_1 (byte *dest, int len) {
3093 ((byte*)dest) [0] = 0;
3096 internal static unsafe void bzero_aligned_2 (byte *dest, int len) {
3097 ((short*)dest) [0] = 0;
3100 internal static unsafe void bzero_aligned_4 (byte *dest, int len) {
3101 ((int*)dest) [0] = 0;
3104 internal static unsafe void bzero_aligned_8 (byte *dest, int len) {
3105 ((long*)dest) [0] = 0;
3108 internal static unsafe void memcpy_aligned_1 (byte *dest, byte *src, int size) {
3109 ((byte*)dest) [0] = ((byte*)src) [0];
3112 internal static unsafe void memcpy_aligned_2 (byte *dest, byte *src, int size) {
3113 ((short*)dest) [0] = ((short*)src) [0];
3116 internal static unsafe void memcpy_aligned_4 (byte *dest, byte *src, int size) {
3117 ((int*)dest) [0] = ((int*)src) [0];
3120 internal static unsafe void memcpy_aligned_8 (byte *dest, byte *src, int size) {
3121 ((long*)dest) [0] = ((long*)src) [0];
3124 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3125 // Same rules as for memcpy, but with the premise that
3126 // chars can only be aligned to even addresses if their
3127 // enclosing types are correctly aligned
3128 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3129 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3130 ((short*)dest) [0] = ((short*)src) [0];
3135 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3136 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3140 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3143 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3147 for (int i = count; i > 0; i--) {
3154 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3156 fixed (char* dest = target, src = source)
3157 CharCopy (dest + targetIndex, src + sourceIndex, count);
3160 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3162 fixed (char* dest = target, src = source)
3163 CharCopy (dest + targetIndex, src + sourceIndex, count);
3166 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3167 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3169 fixed (char* dest = target, src = source)
3170 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3173 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3174 unsafe public extern String (char *value);
3176 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3177 unsafe public extern String (char *value, int startIndex, int length);
3179 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3180 unsafe public extern String (sbyte *value);
3182 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3183 unsafe public extern String (sbyte *value, int startIndex, int length);
3185 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3186 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3188 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3189 public extern String (char [] value, int startIndex, int length);
3191 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3192 public extern String (char [] value);
3194 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3195 public extern String (char c, int count);
3197 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3198 internal extern static String InternalAllocateStr (int length);
3200 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3201 private extern static string InternalIntern (string str);
3203 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3204 private extern static string InternalIsInterned (string str);
3206 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3207 private extern static int GetLOSLimit ();