6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
40 // Ensure that argument checks on methods don't overflow
44 using System.Collections;
45 using System.Globalization;
46 using System.Runtime.CompilerServices;
48 using System.Collections.Generic;
49 using System.Runtime.ConstrainedExecution;
50 using System.Runtime.InteropServices;
51 using Mono.Globalization.Unicode;
58 [StructLayout (LayoutKind.Sequential)]
59 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
61 [NonSerialized] private int length;
62 [NonSerialized] private char start_char;
64 public static readonly String Empty = "";
66 internal static readonly int LOS_limit = GetLOSLimit ();
68 public static unsafe bool Equals (string a, string b)
70 if ((a as object) == (b as object))
73 if (a == null || b == null)
81 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
86 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
87 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
88 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
89 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
98 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
99 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
108 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
116 return len == 0 || *s1_ptr == *s2_ptr;
120 public static bool operator == (String a, String b)
122 return Equals (a, b);
125 public static bool operator != (String a, String b)
127 return !Equals (a, b);
130 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
131 public override bool Equals (Object obj)
133 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
137 public bool Equals (String value)
139 return Equals (this, value);
142 [IndexerName ("Chars")]
143 public unsafe char this [int index] {
145 if (index < 0 || index >= length)
146 throw new IndexOutOfRangeException ();
147 fixed (char* c = &start_char)
152 public Object Clone ()
157 public TypeCode GetTypeCode ()
159 return TypeCode.String;
162 public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
164 if (destination == null)
165 throw new ArgumentNullException ("destination");
167 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
168 if (destinationIndex < 0)
169 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
171 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
172 if (sourceIndex > Length - count)
173 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
174 if (destinationIndex > destination.Length - count)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
177 fixed (char* dest = destination, src = this)
178 CharCopy (dest + destinationIndex, src + sourceIndex, count);
181 public char[] ToCharArray ()
183 return ToCharArray (0, length);
186 public unsafe char[] ToCharArray (int startIndex, int length)
189 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
191 throw new ArgumentOutOfRangeException ("length", "< 0");
192 if (startIndex > this.length - length)
193 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
195 char[] tmp = new char [length];
196 fixed (char* dest = tmp, src = this)
197 CharCopy (dest, src + startIndex, length);
201 public String [] Split (params char [] separator)
203 return Split (separator, int.MaxValue, 0);
206 public String[] Split (char[] separator, int count)
208 return Split (separator, count, 0);
212 public String[] Split (char[] separator, StringSplitOptions options)
214 return Split (separator, Int32.MaxValue, options);
218 public String[] Split (char[] separator, int count, StringSplitOptions options)
221 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
222 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
223 throw new ArgumentException ("Illegal enum value: " + options + ".");
226 return new String[0];
231 new String[1] { this };
234 return SplitByCharacters (separator, count, options != 0);
238 public String[] Split (string[] separator, StringSplitOptions options)
240 return Split (separator, Int32.MaxValue, options);
244 public String[] Split (string[] separator, int count, StringSplitOptions options)
247 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
248 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
249 throw new ArgumentException ("Illegal enum value: " + options + ".");
254 new String[1] { this };
257 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
259 if (separator == null || separator.Length == 0)
260 return SplitByCharacters (null, count, removeEmpty);
262 if (Length == 0 && removeEmpty)
263 return new String [0];
265 List<String> arr = new List<String> ();
269 while (pos < this.Length) {
271 int matchPos = Int32.MaxValue;
273 // Find the first position where any of the separators matches
274 for (int i = 0; i < separator.Length; ++i) {
275 string sep = separator [i];
276 if (sep == null || sep.Length == 0)
279 int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
280 if (match >= 0 && match < matchPos) {
286 if (matchIndex == -1)
289 if (!(matchPos == pos && removeEmpty)) {
290 if (arr.Count == count - 1)
292 arr.Add (this.Substring (pos, matchPos - pos));
295 pos = matchPos + separator [matchIndex].Length;
301 return new String [] { this };
303 // string contained only separators
304 if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
305 return new String [0];
307 if (!(removeEmpty && pos == this.Length))
308 arr.Add (this.Substring (pos));
310 return arr.ToArray ();
313 // .NET 2.0 compatibility only
314 #if !NET_4_0 && !MOONLIGHT && !MOBILE
315 static readonly char[] WhiteChars = {
316 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
317 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
318 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
319 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
320 (char) 0x3000, (char) 0xFEFF
324 unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
326 #if !NET_4_0 && !MOONLIGHT && !MOBILE
327 if (sep == null || sep.Length == 0)
331 int[] split_points = null;
332 int total_points = 0;
335 if (sep == null || sep.Length == 0) {
336 fixed (char* src = this) {
341 if (char.IsWhiteSpace (*src_ptr++)) {
342 if (split_points == null) {
343 split_points = new int[8];
344 } else if (split_points.Length == total_points) {
345 Array.Resize (ref split_points, split_points.Length * 2);
348 split_points[total_points++] = Length - len;
349 if (total_points == count && !removeEmpty)
352 } while (len-- != 0);
355 fixed (char* src = this) {
356 fixed (char* sep_src = sep) {
358 char* sep_ptr_end = sep_src + sep.Length;
361 char* sep_ptr = sep_src;
363 if (*sep_ptr++ == *src_ptr) {
364 if (split_points == null) {
365 split_points = new int[8];
366 } else if (split_points.Length == total_points) {
367 Array.Resize (ref split_points, split_points.Length * 2);
370 split_points[total_points++] = Length - len;
371 if (total_points == count && !removeEmpty)
376 } while (sep_ptr != sep_ptr_end);
379 } while (len-- != 0);
384 if (total_points == 0)
385 return new string[] { this };
387 var res = new string[Math.Min (total_points, count) + 1];
391 for (; i < total_points; ++i) {
392 var start = split_points[i];
393 res[i] = SubstringUnchecked (prev_index, start - prev_index);
394 prev_index = start + 1;
397 res[i] = SubstringUnchecked (prev_index, Length - prev_index);
401 for (; i < total_points; ++i) {
402 var start = split_points[i];
403 length = start - prev_index;
408 res[used++] = SubstringUnchecked (prev_index, length);
411 prev_index = start + 1;
414 length = Length - prev_index;
416 res[used++] = SubstringUnchecked (prev_index, length);
418 if (used != res.Length)
419 Array.Resize (ref res, used);
425 public String Substring (int startIndex)
429 if (startIndex < 0 || startIndex > this.length)
430 throw new ArgumentOutOfRangeException ("startIndex");
432 return SubstringUnchecked (startIndex, this.length - startIndex);
435 public String Substring (int startIndex, int length)
438 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
440 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
441 if (startIndex > this.length)
442 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
443 if (startIndex > this.length - length)
444 throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
445 if (startIndex == 0 && length == this.length)
448 return SubstringUnchecked (startIndex, length);
451 // This method is used by StringBuilder.ToString() and is expected to
452 // always create a new string object (or return String.Empty).
453 internal unsafe String SubstringUnchecked (int startIndex, int length)
458 string tmp = InternalAllocateStr (length);
459 fixed (char* dest = tmp, src = this) {
460 CharCopy (dest, src + startIndex, length);
465 public String Trim ()
469 int start = FindNotWhiteSpace (0, length, 1);
474 int end = FindNotWhiteSpace (length - 1, start, -1);
476 int newLength = end - start + 1;
477 if (newLength == length)
480 return SubstringUnchecked (start, newLength);
483 public String Trim (params char[] trimChars)
485 if (trimChars == null || trimChars.Length == 0)
490 int start = FindNotInTable (0, length, 1, trimChars);
495 int end = FindNotInTable (length - 1, start, -1, trimChars);
497 int newLength = end - start + 1;
498 if (newLength == length)
501 return SubstringUnchecked (start, newLength);
504 public String TrimStart (params char[] trimChars)
509 if (trimChars == null || trimChars.Length == 0)
510 start = FindNotWhiteSpace (0, length, 1);
512 start = FindNotInTable (0, length, 1, trimChars);
517 return SubstringUnchecked (start, length - start);
520 public String TrimEnd (params char[] trimChars)
525 if (trimChars == null || trimChars.Length == 0)
526 end = FindNotWhiteSpace (length - 1, -1, -1);
528 end = FindNotInTable (length - 1, -1, -1, trimChars);
534 return SubstringUnchecked (0, end);
537 unsafe int FindNotWhiteSpace (int pos, int target, int change)
539 #if NET_4_0 || NET_2_1
540 fixed (char* src = this) {
541 while (pos != target) {
542 if (!char.IsWhiteSpace (src[pos]))
549 while (pos != target) {
553 if (c < 0x9 || c > 0xD)
558 if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
559 if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
560 if (c < 0x2000 || c > 0x200B)
570 private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
572 fixed (char* tablePtr = table, thisPtr = this) {
573 while (pos != target) {
574 char c = thisPtr[pos];
576 while (x < table.Length) {
577 if (c == tablePtr[x])
581 if (x == table.Length)
589 public static int Compare (String strA, String strB)
591 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
594 public static int Compare (String strA, String strB, bool ignoreCase)
596 return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
599 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
602 throw new ArgumentNullException ("culture");
604 return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
607 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
609 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
612 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
614 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
617 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
620 throw new ArgumentNullException ("culture");
622 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
623 throw new ArgumentOutOfRangeException ();
635 else if (strB == null) {
639 CompareOptions compopts;
642 compopts = CompareOptions.IgnoreCase;
644 compopts = CompareOptions.None;
646 // Need to cap the requested length to the
647 // length of the string, because
648 // CompareInfo.Compare will insist that length
649 // <= (string.Length - offset)
654 if (length > (strA.Length - indexA)) {
655 len1 = strA.Length - indexA;
658 if (length > (strB.Length - indexB)) {
659 len2 = strB.Length - indexB;
662 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
663 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
666 public static int Compare (string strA, string strB, StringComparison comparisonType)
668 switch (comparisonType) {
669 case StringComparison.CurrentCulture:
670 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
671 case StringComparison.CurrentCultureIgnoreCase:
672 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
673 case StringComparison.InvariantCulture:
674 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
675 case StringComparison.InvariantCultureIgnoreCase:
676 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
677 case StringComparison.Ordinal:
678 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
679 case StringComparison.OrdinalIgnoreCase:
680 return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
682 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
683 throw new ArgumentException (msg, "comparisonType");
687 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
689 switch (comparisonType) {
690 case StringComparison.CurrentCulture:
691 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
692 case StringComparison.CurrentCultureIgnoreCase:
693 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
694 case StringComparison.InvariantCulture:
695 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
696 case StringComparison.InvariantCultureIgnoreCase:
697 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
698 case StringComparison.Ordinal:
699 return CompareOrdinal (strA, indexA, strB, indexB, length);
700 case StringComparison.OrdinalIgnoreCase:
701 return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
703 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
704 throw new ArgumentException (msg, "comparisonType");
708 public static bool Equals (string a, string b, StringComparison comparisonType)
710 return String.Compare (a, b, comparisonType) == 0;
713 public bool Equals (string value, StringComparison comparisonType)
715 return String.Compare (value, this, comparisonType) == 0;
718 public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
721 throw new ArgumentNullException ("culture");
723 return culture.CompareInfo.Compare (strA, strB, options);
726 public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
729 throw new ArgumentNullException ("culture");
734 if (length > (strA.Length - indexA))
735 len1 = strA.Length - indexA;
737 if (length > (strB.Length - indexB))
738 len2 = strB.Length - indexB;
740 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
743 public int CompareTo (Object value)
748 if (!(value is String))
749 throw new ArgumentException ();
751 return String.Compare (this, (String) value);
754 public int CompareTo (String strB)
759 return Compare (this, strB);
762 public static int CompareOrdinal (String strA, String strB)
764 return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
767 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
769 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
770 throw new ArgumentOutOfRangeException ();
772 return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
775 internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
777 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
778 throw new ArgumentOutOfRangeException ();
780 return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
783 internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
790 } else if (strB == null) {
793 int lengthA = Math.Min (lenA, strA.Length - indexA);
794 int lengthB = Math.Min (lenB, strB.Length - indexB);
796 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
799 fixed (char* aptr = strA, bptr = strB) {
800 char* ap = aptr + indexA;
801 char* end = ap + Math.Min (lengthA, lengthB);
802 char* bp = bptr + indexB;
809 return lengthA - lengthB;
813 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
815 // Same as above, but checks versus uppercase characters
821 } else if (strB == null) {
824 int lengthA = Math.Min (lenA, strA.Length - indexA);
825 int lengthB = Math.Min (lenB, strB.Length - indexB);
827 if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
830 fixed (char* aptr = strA, bptr = strB) {
831 char* ap = aptr + indexA;
832 char* end = ap + Math.Min (lengthA, lengthB);
833 char* bp = bptr + indexB;
836 char c1 = Char.ToUpperInvariant (*ap);
837 char c2 = Char.ToUpperInvariant (*bp);
844 return lengthA - lengthB;
848 public bool EndsWith (String value)
851 throw new ArgumentNullException ("value");
853 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
856 public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
859 throw new ArgumentNullException ("value");
861 culture = CultureInfo.CurrentCulture;
863 return culture.CompareInfo.IsSuffix (this, value,
864 ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
867 // Following methods are culture-insensitive
868 public int IndexOfAny (char [] anyOf)
871 throw new ArgumentNullException ();
872 if (this.length == 0)
875 return IndexOfAnyUnchecked (anyOf, 0, this.length);
878 public int IndexOfAny (char [] anyOf, int startIndex)
881 throw new ArgumentNullException ();
882 if (startIndex < 0 || startIndex > this.length)
883 throw new ArgumentOutOfRangeException ();
885 return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
888 public int IndexOfAny (char [] anyOf, int startIndex, int count)
891 throw new ArgumentNullException ();
892 if (startIndex < 0 || startIndex > this.length)
893 throw new ArgumentOutOfRangeException ();
894 if (count < 0 || startIndex > this.length - count)
895 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
897 return IndexOfAnyUnchecked (anyOf, startIndex, count);
900 private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
902 if (anyOf.Length == 0)
905 if (anyOf.Length == 1)
906 return IndexOfUnchecked (anyOf[0], startIndex, count);
908 fixed (char* any = anyOf) {
912 char* end_any_ptr = any + anyOf.Length;
914 while (++any_ptr != end_any_ptr) {
915 if (*any_ptr > highest) {
920 if (*any_ptr < lowest)
924 fixed (char* start = &start_char) {
925 char* ptr = start + startIndex;
926 char* end_ptr = ptr + count;
928 while (ptr != end_ptr) {
929 if (*ptr > highest || *ptr < lowest) {
935 return (int)(ptr - start);
938 while (++any_ptr != end_any_ptr) {
939 if (*ptr == *any_ptr)
940 return (int)(ptr - start);
951 public int IndexOf (string value, StringComparison comparisonType)
953 return IndexOf (value, 0, this.Length, comparisonType);
956 public int IndexOf (string value, int startIndex, StringComparison comparisonType)
958 return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
961 public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
963 switch (comparisonType) {
964 case StringComparison.CurrentCulture:
965 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
966 case StringComparison.CurrentCultureIgnoreCase:
967 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
968 case StringComparison.InvariantCulture:
969 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
970 case StringComparison.InvariantCultureIgnoreCase:
971 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
972 case StringComparison.Ordinal:
973 return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
974 case StringComparison.OrdinalIgnoreCase:
975 return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
977 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
978 throw new ArgumentException (msg, "comparisonType");
982 internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
985 throw new ArgumentNullException ("value");
987 throw new ArgumentOutOfRangeException ("startIndex");
988 if (count < 0 || (this.length - startIndex) < count)
989 throw new ArgumentOutOfRangeException ("count");
991 if (options == CompareOptions.Ordinal)
992 return IndexOfOrdinalUnchecked (value, startIndex, count);
993 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
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.Ordinal);
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)
1493 return IndexOf (value) != -1;
1496 public static bool IsNullOrEmpty (String value)
1498 return (value == null) || (value.Length == 0);
1502 public string Normalize ()
1504 return Normalization.Normalize (this, 0);
1507 public string Normalize (NormalizationForm normalizationForm)
1509 switch (normalizationForm) {
1511 return Normalization.Normalize (this, 0);
1512 case NormalizationForm.FormD:
1513 return Normalization.Normalize (this, 1);
1514 case NormalizationForm.FormKC:
1515 return Normalization.Normalize (this, 2);
1516 case NormalizationForm.FormKD:
1517 return Normalization.Normalize (this, 3);
1521 public bool IsNormalized ()
1523 return Normalization.IsNormalized (this, 0);
1526 public bool IsNormalized (NormalizationForm normalizationForm)
1528 switch (normalizationForm) {
1530 return Normalization.IsNormalized (this, 0);
1531 case NormalizationForm.FormD:
1532 return Normalization.IsNormalized (this, 1);
1533 case NormalizationForm.FormKC:
1534 return Normalization.IsNormalized (this, 2);
1535 case NormalizationForm.FormKD:
1536 return Normalization.IsNormalized (this, 3);
1541 public string Remove (int startIndex)
1544 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1545 if (startIndex >= this.length)
1546 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1548 return Remove (startIndex, this.length - startIndex);
1551 public String PadLeft (int totalWidth)
1553 return PadLeft (totalWidth, ' ');
1556 public unsafe String PadLeft (int totalWidth, char paddingChar)
1558 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1561 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1563 if (totalWidth < this.length)
1565 if (totalWidth == 0)
1568 String tmp = InternalAllocateStr (totalWidth);
1570 fixed (char* dest = tmp, src = this) {
1571 char* padPos = dest;
1572 char* padTo = dest + (totalWidth - length);
1573 while (padPos != padTo)
1574 *padPos++ = paddingChar;
1576 CharCopy (padTo, src, length);
1581 public String PadRight (int totalWidth)
1583 return PadRight (totalWidth, ' ');
1586 public unsafe String PadRight (int totalWidth, char paddingChar)
1588 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1591 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1593 if (totalWidth < this.length)
1595 if (totalWidth == 0)
1598 String tmp = InternalAllocateStr (totalWidth);
1600 fixed (char* dest = tmp, src = this) {
1601 CharCopy (dest, src, length);
1603 char* padPos = dest + length;
1604 char* padTo = dest + totalWidth;
1605 while (padPos != padTo)
1606 *padPos++ = paddingChar;
1611 public bool StartsWith (String value)
1614 throw new ArgumentNullException ("value");
1616 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1619 [ComVisible (false)]
1620 public bool StartsWith (string value, StringComparison comparisonType)
1623 throw new ArgumentNullException ("value");
1625 switch (comparisonType) {
1626 case StringComparison.CurrentCulture:
1627 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1628 case StringComparison.CurrentCultureIgnoreCase:
1629 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1630 case StringComparison.InvariantCulture:
1631 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1632 case StringComparison.InvariantCultureIgnoreCase:
1633 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1634 case StringComparison.Ordinal:
1635 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1636 case StringComparison.OrdinalIgnoreCase:
1637 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1639 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1640 throw new ArgumentException (msg, "comparisonType");
1644 [ComVisible (false)]
1645 public bool EndsWith (string value, StringComparison comparisonType)
1648 throw new ArgumentNullException ("value");
1650 switch (comparisonType) {
1651 case StringComparison.CurrentCulture:
1652 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1653 case StringComparison.CurrentCultureIgnoreCase:
1654 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1655 case StringComparison.InvariantCulture:
1656 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1657 case StringComparison.InvariantCultureIgnoreCase:
1658 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1659 case StringComparison.Ordinal:
1660 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1661 case StringComparison.OrdinalIgnoreCase:
1662 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1664 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
1665 throw new ArgumentException (msg, "comparisonType");
1669 public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1671 if (culture == null)
1672 culture = CultureInfo.CurrentCulture;
1674 return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
1677 // Following method is culture-insensitive
1678 public unsafe String Replace (char oldChar, char newChar)
1680 if (this.length == 0 || oldChar == newChar)
1683 int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
1684 if (start_pos == -1)
1690 string tmp = InternalAllocateStr (length);
1691 fixed (char* dest = tmp, src = &start_char) {
1693 CharCopy (dest, src, start_pos);
1695 char* end_ptr = dest + length;
1696 char* dest_ptr = dest + start_pos;
1697 char* src_ptr = src + start_pos;
1699 while (dest_ptr != end_ptr) {
1700 if (*src_ptr == oldChar)
1701 *dest_ptr = newChar;
1703 *dest_ptr = *src_ptr;
1712 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1713 public String Replace (String oldValue, String newValue)
1715 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1716 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1718 if (oldValue == null)
1719 throw new ArgumentNullException ("oldValue");
1721 if (oldValue.Length == 0)
1722 throw new ArgumentException ("oldValue is the empty string.");
1724 if (this.Length == 0)
1727 if (newValue == null)
1730 return ReplaceUnchecked (oldValue, newValue);
1733 private unsafe String ReplaceUnchecked (String oldValue, String newValue)
1735 if (oldValue.length > length)
1737 if (oldValue.length == 1 && newValue.length == 1) {
1738 return Replace (oldValue[0], newValue[0]);
1739 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1740 // because the length of the result would be this.length and length calculation unneccesary
1743 const int maxValue = 200; // Allocate 800 byte maximum
1744 int* dat = stackalloc int[maxValue];
1745 fixed (char* source = this, replace = newValue) {
1746 int i = 0, count = 0;
1747 while (i < length) {
1748 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1752 if (count < maxValue)
1753 dat[count++] = found;
1755 return ReplaceFallback (oldValue, newValue, maxValue);
1757 i = found + oldValue.length;
1761 int nlen = this.length + ((newValue.length - oldValue.length) * count);
1762 String tmp = InternalAllocateStr (nlen);
1764 int curPos = 0, lastReadPos = 0;
1765 fixed (char* dest = tmp) {
1766 for (int j = 0; j < count; j++) {
1767 int precopy = dat[j] - lastReadPos;
1768 CharCopy (dest + curPos, source + lastReadPos, precopy);
1770 lastReadPos = dat[j] + oldValue.length;
1771 CharCopy (dest + curPos, replace, newValue.length);
1772 curPos += newValue.length;
1774 CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
1780 private String ReplaceFallback (String oldValue, String newValue, int testedCount)
1782 int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
1783 StringBuilder sb = new StringBuilder (lengthEstimate);
1784 for (int i = 0; i < length;) {
1785 int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
1787 sb.Append (SubstringUnchecked (i, length - i));
1790 sb.Append (SubstringUnchecked (i, found - i));
1791 sb.Append (newValue);
1792 i = found + oldValue.Length;
1794 return sb.ToString ();
1798 public unsafe String Remove (int startIndex, int count)
1801 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1803 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1804 if (startIndex > this.length - count)
1805 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1807 String tmp = InternalAllocateStr (this.length - count);
1809 fixed (char *dest = tmp, src = this) {
1811 CharCopy (dst, src, startIndex);
1812 int skip = startIndex + count;
1814 CharCopy (dst, src + skip, length - skip);
1819 public String ToLower ()
1821 return ToLower (CultureInfo.CurrentCulture);
1824 public String ToLower (CultureInfo culture)
1826 if (culture == null)
1827 throw new ArgumentNullException ("culture");
1829 if (culture.LCID == 0x007F) // Invariant
1830 return ToLowerInvariant ();
1832 return culture.TextInfo.ToLower (this);
1835 public unsafe String ToLowerInvariant ()
1840 string tmp = InternalAllocateStr (length);
1841 fixed (char* source = &start_char, dest = tmp) {
1843 char* destPtr = (char*)dest;
1844 char* sourcePtr = (char*)source;
1846 for (int n = 0; n < length; n++) {
1847 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1855 public String ToUpper ()
1857 return ToUpper (CultureInfo.CurrentCulture);
1860 public String ToUpper (CultureInfo culture)
1862 if (culture == null)
1863 throw new ArgumentNullException ("culture");
1865 if (culture.LCID == 0x007F) // Invariant
1866 return ToUpperInvariant ();
1868 return culture.TextInfo.ToUpper (this);
1871 public unsafe String ToUpperInvariant ()
1876 string tmp = InternalAllocateStr (length);
1877 fixed (char* source = &start_char, dest = tmp) {
1879 char* destPtr = (char*)dest;
1880 char* sourcePtr = (char*)source;
1882 for (int n = 0; n < length; n++) {
1883 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1891 public override String ToString ()
1896 public String ToString (IFormatProvider provider)
1901 public static String Format (String format, Object arg0)
1903 return Format (null, format, new Object[] {arg0});
1906 public static String Format (String format, Object arg0, Object arg1)
1908 return Format (null, format, new Object[] {arg0, arg1});
1911 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1913 return Format (null, format, new Object[] {arg0, arg1, arg2});
1916 public static string Format (string format, params object[] args)
1918 return Format (null, format, args);
1921 public static string Format (IFormatProvider provider, string format, params object[] args)
1923 StringBuilder b = FormatHelper (null, provider, format, args);
1924 return b.ToString ();
1927 internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1930 throw new ArgumentNullException ("format");
1932 throw new ArgumentNullException ("args");
1934 if (result == null) {
1935 /* Try to approximate the size of result to avoid reallocations */
1939 for (i = 0; i < args.Length; ++i) {
1940 string s = args [i] as string;
1946 if (i == args.Length)
1947 result = new StringBuilder (len + format.length);
1949 result = new StringBuilder ();
1954 while (ptr < format.length) {
1955 char c = format[ptr ++];
1958 result.Append (format, start, ptr - start - 1);
1960 // check for escaped open bracket
1962 if (format[ptr] == '{') {
1973 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1974 if (n >= args.Length)
1975 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1979 object arg = args[n];
1982 ICustomFormatter formatter = null;
1983 if (provider != null)
1984 formatter = provider.GetFormat (typeof (ICustomFormatter))
1985 as ICustomFormatter;
1988 else if (formatter != null)
1989 str = formatter.Format (arg_format, arg, provider);
1990 else if (arg is IFormattable)
1991 str = ((IFormattable)arg).ToString (arg_format, provider);
1993 str = arg.ToString ();
1995 // pad formatted string and append to result
1997 if (width > str.length) {
1998 const char padchar = ' ';
1999 int padlen = width - str.length;
2002 result.Append (str);
2003 result.Append (padchar, padlen);
2006 result.Append (padchar, padlen);
2007 result.Append (str);
2011 result.Append (str);
2015 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
2016 result.Append (format, start, ptr - start - 1);
2019 else if (c == '}') {
2020 throw new FormatException ("Input string was not in a correct format.");
2024 if (start < format.length)
2025 result.Append (format, start, format.Length - start);
2030 public unsafe static String Copy (String str)
2033 throw new ArgumentNullException ("str");
2035 int length = str.length;
2037 String tmp = InternalAllocateStr (length);
2039 fixed (char *dest = tmp, src = str) {
2040 CharCopy (dest, src, length);
2046 public static String Concat (Object arg0)
2051 return arg0.ToString ();
2054 public static String Concat (Object arg0, Object arg1)
2056 return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
2059 public static String Concat (Object arg0, Object arg1, Object arg2)
2065 s1 = arg0.ToString ();
2070 s2 = arg1.ToString ();
2075 s3 = arg2.ToString ();
2077 return Concat (s1, s2, s3);
2080 [CLSCompliant(false)]
2081 public static String Concat (Object arg0, Object arg1, Object arg2,
2082 Object arg3, __arglist)
2084 string s1, s2, s3, s4;
2089 s1 = arg0.ToString ();
2094 s2 = arg1.ToString ();
2099 s3 = arg2.ToString ();
2101 ArgIterator iter = new ArgIterator (__arglist);
2102 int argCount = iter.GetRemainingCount();
2104 StringBuilder sb = new StringBuilder ();
2106 sb.Append (arg3.ToString ());
2108 for (int i = 0; i < argCount; i++) {
2109 TypedReference typedRef = iter.GetNextArg ();
2110 sb.Append (TypedReference.ToObject (typedRef));
2113 s4 = sb.ToString ();
2115 return Concat (s1, s2, s3, s4);
2118 public unsafe static String Concat (String str0, String str1)
2120 if (str0 == null || str0.Length == 0) {
2121 if (str1 == null || str1.Length == 0)
2126 if (str1 == null || str1.Length == 0)
2129 String tmp = InternalAllocateStr (str0.length + str1.length);
2131 fixed (char *dest = tmp, src = str0)
2132 CharCopy (dest, src, str0.length);
2133 fixed (char *dest = tmp, src = str1)
2134 CharCopy (dest + str0.Length, src, str1.length);
2139 public unsafe static String Concat (String str0, String str1, String str2)
2141 if (str0 == null || str0.Length == 0){
2142 if (str1 == null || str1.Length == 0){
2143 if (str2 == null || str2.Length == 0)
2147 if (str2 == null || str2.Length == 0)
2152 if (str1 == null || str1.Length == 0){
2153 if (str2 == null || str2.Length == 0)
2158 if (str2 == null || str2.Length == 0)
2163 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
2165 if (str0.Length != 0) {
2166 fixed (char *dest = tmp, src = str0) {
2167 CharCopy (dest, src, str0.length);
2170 if (str1.Length != 0) {
2171 fixed (char *dest = tmp, src = str1) {
2172 CharCopy (dest + str0.Length, src, str1.length);
2175 if (str2.Length != 0) {
2176 fixed (char *dest = tmp, src = str2) {
2177 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2184 public unsafe static String Concat (String str0, String str1, String str2, String str3)
2186 if (str0 == null && str1 == null && str2 == null && str3 == null)
2198 String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
2200 if (str0.Length != 0) {
2201 fixed (char *dest = tmp, src = str0) {
2202 CharCopy (dest, src, str0.length);
2205 if (str1.Length != 0) {
2206 fixed (char *dest = tmp, src = str1) {
2207 CharCopy (dest + str0.Length, src, str1.length);
2210 if (str2.Length != 0) {
2211 fixed (char *dest = tmp, src = str2) {
2212 CharCopy (dest + str0.Length + str1.Length, src, str2.length);
2215 if (str3.Length != 0) {
2216 fixed (char *dest = tmp, src = str3) {
2217 CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
2224 public static String Concat (params Object[] args)
2227 throw new ArgumentNullException ("args");
2229 int argLen = args.Length;
2233 string [] strings = new string [argLen];
2235 for (int i = 0; i < argLen; i++) {
2236 if (args[i] != null) {
2237 strings[i] = args[i].ToString ();
2238 len += strings[i].length;
2242 return ConcatInternal (strings, len);
2245 public static String Concat (params String[] values)
2248 throw new ArgumentNullException ("values");
2251 for (int i = 0; i < values.Length; i++) {
2252 String s = values[i];
2257 return ConcatInternal (values, len);
2260 private static unsafe String ConcatInternal (String[] values, int length)
2265 String tmp = InternalAllocateStr (length);
2267 fixed (char* dest = tmp) {
2269 for (int i = 0; i < values.Length; i++) {
2270 String source = values[i];
2271 if (source != null) {
2272 fixed (char* src = source) {
2273 CharCopy (dest + pos, src, source.length);
2275 pos += source.Length;
2282 public unsafe String Insert (int startIndex, String value)
2285 throw new ArgumentNullException ("value");
2287 if (startIndex < 0 || startIndex > this.length)
2288 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2290 if (value.Length == 0)
2292 if (this.Length == 0)
2294 String tmp = InternalAllocateStr (this.length + value.length);
2296 fixed (char *dest = tmp, src = this, val = value) {
2298 CharCopy (dst, src, startIndex);
2300 CharCopy (dst, val, value.length);
2301 dst += value.length;
2302 CharCopy (dst, src + startIndex, length - startIndex);
2307 public static string Intern (string str)
2310 throw new ArgumentNullException ("str");
2312 return InternalIntern (str);
2315 public static string IsInterned (string str)
2318 throw new ArgumentNullException ("str");
2320 return InternalIsInterned (str);
2323 #if NET_4_0 || MOONLIGHT || MOBILE
2324 public static string Join (string separator, params string [] value)
2326 public static string Join (string separator, string [] value)
2330 throw new ArgumentNullException ("value");
2331 if (separator == null)
2334 return JoinUnchecked (separator, value, 0, value.Length);
2337 public static string Join (string separator, string[] value, int startIndex, int count)
2340 throw new ArgumentNullException ("value");
2342 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2344 throw new ArgumentOutOfRangeException ("count", "< 0");
2345 if (startIndex > value.Length - count)
2346 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2348 if (startIndex == value.Length)
2350 if (separator == null)
2353 return JoinUnchecked (separator, value, startIndex, count);
2356 private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
2358 // Unchecked parameters
2359 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2360 // separator and value must not be null
2363 int maxIndex = startIndex + count;
2364 // Precount the number of characters that the resulting string will have
2365 for (int i = startIndex; i < maxIndex; i++) {
2366 String s = value[i];
2370 length += separator.length * (count - 1);
2374 String tmp = InternalAllocateStr (length);
2377 fixed (char* dest = tmp, sepsrc = separator) {
2378 // Copy each string from value except the last one and add a separator for each
2380 for (int i = startIndex; i < maxIndex; i++) {
2381 String source = value[i];
2382 if (source != null) {
2383 if (source.Length > 0) {
2384 fixed (char* src = source)
2385 CharCopy (dest + pos, src, source.Length);
2386 pos += source.Length;
2389 if (separator.Length > 0) {
2390 CharCopy (dest + pos, sepsrc, separator.Length);
2391 pos += separator.Length;
2394 // Append last string that does not get an additional separator
2395 String sourceLast = value[maxIndex];
2396 if (sourceLast != null) {
2397 if (sourceLast.Length > 0) {
2398 fixed (char* src = sourceLast)
2399 CharCopy (dest + pos, src, sourceLast.Length);
2406 bool IConvertible.ToBoolean (IFormatProvider provider)
2408 return Convert.ToBoolean (this, provider);
2411 byte IConvertible.ToByte (IFormatProvider provider)
2413 return Convert.ToByte (this, provider);
2416 char IConvertible.ToChar (IFormatProvider provider)
2418 return Convert.ToChar (this, provider);
2421 DateTime IConvertible.ToDateTime (IFormatProvider provider)
2423 return Convert.ToDateTime (this, provider);
2426 decimal IConvertible.ToDecimal (IFormatProvider provider)
2428 return Convert.ToDecimal (this, provider);
2431 double IConvertible.ToDouble (IFormatProvider provider)
2433 return Convert.ToDouble (this, provider);
2436 short IConvertible.ToInt16 (IFormatProvider provider)
2438 return Convert.ToInt16 (this, provider);
2441 int IConvertible.ToInt32 (IFormatProvider provider)
2443 return Convert.ToInt32 (this, provider);
2446 long IConvertible.ToInt64 (IFormatProvider provider)
2448 return Convert.ToInt64 (this, provider);
2451 sbyte IConvertible.ToSByte (IFormatProvider provider)
2453 return Convert.ToSByte (this, provider);
2456 float IConvertible.ToSingle (IFormatProvider provider)
2458 return Convert.ToSingle (this, provider);
2461 object IConvertible.ToType (Type targetType, IFormatProvider provider)
2463 if (targetType == null)
2464 throw new ArgumentNullException ("type");
2465 return Convert.ToType (this, targetType, provider, false);
2468 ushort IConvertible.ToUInt16 (IFormatProvider provider)
2470 return Convert.ToUInt16 (this, provider);
2473 uint IConvertible.ToUInt32 (IFormatProvider provider)
2475 return Convert.ToUInt32 (this, provider);
2478 ulong IConvertible.ToUInt64 (IFormatProvider provider)
2480 return Convert.ToUInt64 (this, provider);
2489 public CharEnumerator GetEnumerator ()
2491 return new CharEnumerator (this);
2494 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
2496 return new CharEnumerator (this);
2499 IEnumerator IEnumerable.GetEnumerator ()
2501 return new CharEnumerator (this);
2504 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
2505 out bool left_align, out string format)
2507 int max = str.Length;
2509 // parses format specifier of form:
2513 // N = argument number (non-negative integer)
2515 n = ParseDecimal (str, ref ptr);
2517 throw new FormatException ("Input string was not in a correct format.");
2519 // M = width (non-negative integer)
2521 if (ptr < max && str[ptr] == ',') {
2522 // White space between ',' and number or sign.
2524 while (ptr < max && Char.IsWhiteSpace (str [ptr]))
2528 format = str.Substring (start, ptr - start);
2530 left_align = (ptr < max && str [ptr] == '-');
2534 width = ParseDecimal (str, ref ptr);
2536 throw new FormatException ("Input string was not in a correct format.");
2544 // F = argument format (string)
2546 if (ptr < max && str[ptr] == ':') {
2548 while (ptr < max && str[ptr] != '}')
2551 format += str.Substring (start, ptr - start);
2556 if ((ptr >= max) || str[ptr ++] != '}')
2557 throw new FormatException ("Input string was not in a correct format.");
2560 private static int ParseDecimal (string str, ref int ptr)
2564 int max = str.Length;
2568 if (c < '0' || '9' < c)
2571 n = n * 10 + c - '0';
2575 if (p == ptr || p == max)
2582 internal unsafe void InternalSetChar (int idx, char val)
2584 if ((uint) idx >= (uint) Length)
2585 throw new ArgumentOutOfRangeException ("idx");
2587 fixed (char * pStr = &start_char)
2593 internal unsafe void InternalSetLength (int newLength)
2595 if (newLength > length)
2596 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2598 // zero terminate, we can pass string objects directly via pinvoke
2599 // we also zero the rest of the string, since the new GC needs to be
2600 // able to handle the changing size (it will skip the 0 bytes).
2601 fixed (char * pStr = &start_char) {
2602 char *p = pStr + newLength;
2603 char *end = pStr + length;
2612 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
2613 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2614 public unsafe override int GetHashCode ()
2616 fixed (char * c = this) {
2618 char * end = cc + length - 1;
2620 for (;cc < end; cc += 2) {
2621 h = (h << 5) - h + *cc;
2622 h = (h << 5) - h + cc [1];
2626 h = (h << 5) - h + *cc;
2631 #if MOONLIGHT || MOBILE || NET_4_0
2633 public static string Concat (IEnumerable<string> values)
2636 throw new ArgumentNullException ("values");
2638 var stringList = new List<string> ();
2640 foreach (var v in values){
2646 return ConcatInternal (stringList.ToArray (), len);
2649 [ComVisibleAttribute(false)]
2650 public static string Concat<T> (IEnumerable<T> values)
2653 throw new ArgumentNullException ("values");
2655 var stringList = new List<string> ();
2657 foreach (var v in values){
2658 string sr = v.ToString ();
2660 stringList.Add (sr);
2662 return ConcatInternal (stringList.ToArray (), len);
2665 [ComVisibleAttribute(false)]
2666 public static string Join (string separator, IEnumerable<string> values)
2668 if (separator == null)
2669 return Concat (values);
2672 throw new ArgumentNullException ("values");
2674 var stringList = new List<string> ();
2675 foreach (var v in values)
2678 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2681 [ComVisibleAttribute(false)]
2682 public static string Join (string separator, params object [] values)
2684 if (separator == null)
2685 return Concat (values);
2688 throw new ArgumentNullException ("values");
2690 var strCopy = new string [values.Length];
2692 foreach (var v in values)
2693 strCopy [i++] = v.ToString ();
2695 return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
2698 [ComVisible (false)]
2699 public static string Join<T> (string separator, IEnumerable<T> values)
2701 if (separator == null)
2702 return Concat<T> (values);
2705 throw new ArgumentNullException ("values");
2707 var stringList = new List<string> ();
2708 foreach (var v in values)
2709 stringList.Add (v.ToString ());
2711 return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
2714 public static bool IsNullOrWhiteSpace (string value)
2716 internal static bool IsNullOrWhiteSpace (string value)
2719 if ((value == null) || (value.Length == 0))
2721 foreach (char c in value)
2722 if (!Char.IsWhiteSpace (c))
2727 internal unsafe int GetCaseInsensitiveHashCode ()
2729 fixed (char * c = this) {
2731 char * end = cc + length - 1;
2733 for (;cc < end; cc += 2) {
2734 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2735 h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
2739 h = (h << 5) - h + Char.ToUpperInvariant (*cc);
2744 // Certain constructors are redirected to CreateString methods with
2745 // matching argument list. The this pointer should not be used.
2747 private unsafe String CreateString (sbyte* value)
2752 byte* bytes = (byte*) value;
2756 while (bytes++ [0] != 0)
2758 } catch (NullReferenceException) {
2759 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2760 } catch (AccessViolationException) {
2761 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2764 return CreateString (value, 0, length, null);
2767 private unsafe String CreateString (sbyte* value, int startIndex, int length)
2769 return CreateString (value, startIndex, length, null);
2772 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
2775 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2777 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2778 if (value + startIndex < value)
2779 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2781 bool isDefaultEncoding;
2783 if (isDefaultEncoding = (enc == null)) {
2785 throw new ArgumentNullException ("value");
2789 enc = Encoding.Default;
2792 byte [] bytes = new byte [length];
2795 fixed (byte* bytePtr = bytes)
2797 memcpy (bytePtr, (byte*) (value + startIndex), length);
2798 } catch (NullReferenceException) {
2799 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2800 } catch (AccessViolationException) {
2801 if (!isDefaultEncoding)
2804 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2807 // GetString () is called even when length == 0
2808 return enc.GetString (bytes);
2811 unsafe string CreateString (char *value)
2821 string result = InternalAllocateStr (i);
2824 fixed (char *dest = result) {
2825 CharCopy (dest, value, i);
2831 unsafe string CreateString (char *value, int startIndex, int length)
2836 throw new ArgumentNullException ("value");
2838 throw new ArgumentOutOfRangeException ("startIndex");
2840 throw new ArgumentOutOfRangeException ("length");
2842 string result = InternalAllocateStr (length);
2844 fixed (char *dest = result) {
2845 CharCopy (dest, value + startIndex, length);
2850 unsafe string CreateString (char [] val, int startIndex, int length)
2853 throw new ArgumentNullException ("value");
2855 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2857 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2858 if (startIndex > val.Length - length)
2859 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2863 string result = InternalAllocateStr (length);
2865 fixed (char *dest = result, src = val) {
2866 CharCopy (dest, src + startIndex, length);
2871 unsafe string CreateString (char [] val)
2873 if (val == null || val.Length == 0)
2875 string result = InternalAllocateStr (val.Length);
2877 fixed (char *dest = result, src = val) {
2878 CharCopy (dest, src, val.Length);
2883 unsafe string CreateString (char c, int count)
2886 throw new ArgumentOutOfRangeException ("count");
2889 string result = InternalAllocateStr (count);
2890 fixed (char *dest = result) {
2892 char *end = p + count;
2901 /* helpers used by the runtime as well as above or eslewhere in corlib */
2902 internal static unsafe void memset (byte *dest, int val, int len)
2913 val = val | (val << 8);
2914 val = val | (val << 16);
2917 int rest = (int)dest & 3;
2925 } while (rest != 0);
2928 ((int*)dest) [0] = val;
2929 ((int*)dest) [1] = val;
2930 ((int*)dest) [2] = val;
2931 ((int*)dest) [3] = val;
2936 ((int*)dest) [0] = val;
2948 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2949 /*while (size >= 32) {
2950 // using long is better than int and slower than double
2951 // FIXME: enable this only on correct alignment or on platforms
2952 // that can tolerate unaligned reads/writes of doubles
2953 ((double*)dest) [0] = ((double*)src) [0];
2954 ((double*)dest) [1] = ((double*)src) [1];
2955 ((double*)dest) [2] = ((double*)src) [2];
2956 ((double*)dest) [3] = ((double*)src) [3];
2961 while (size >= 16) {
2962 ((int*)dest) [0] = ((int*)src) [0];
2963 ((int*)dest) [1] = ((int*)src) [1];
2964 ((int*)dest) [2] = ((int*)src) [2];
2965 ((int*)dest) [3] = ((int*)src) [3];
2971 ((int*)dest) [0] = ((int*)src) [0];
2977 ((byte*)dest) [0] = ((byte*)src) [0];
2983 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2985 ((short*)dest) [0] = ((short*)src) [0];
2986 ((short*)dest) [1] = ((short*)src) [1];
2987 ((short*)dest) [2] = ((short*)src) [2];
2988 ((short*)dest) [3] = ((short*)src) [3];
2994 ((short*)dest) [0] = ((short*)src) [0];
3000 ((byte*)dest) [0] = ((byte*)src) [0];
3002 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
3004 ((byte*)dest) [0] = ((byte*)src) [0];
3005 ((byte*)dest) [1] = ((byte*)src) [1];
3006 ((byte*)dest) [2] = ((byte*)src) [2];
3007 ((byte*)dest) [3] = ((byte*)src) [3];
3008 ((byte*)dest) [4] = ((byte*)src) [4];
3009 ((byte*)dest) [5] = ((byte*)src) [5];
3010 ((byte*)dest) [6] = ((byte*)src) [6];
3011 ((byte*)dest) [7] = ((byte*)src) [7];
3017 ((byte*)dest) [0] = ((byte*)src) [0];
3018 ((byte*)dest) [1] = ((byte*)src) [1];
3024 ((byte*)dest) [0] = ((byte*)src) [0];
3027 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
3028 // FIXME: if pointers are not aligned, try to align them
3029 // so a faster routine can be used. Handle the case where
3030 // the pointers can't be reduced to have the same alignment
3031 // (just ignore the issue on x86?)
3032 if ((((int)dest | (int)src) & 3) != 0) {
3033 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
3039 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
3040 ((short*)dest) [0] = ((short*)src) [0];
3045 if ((((int)dest | (int)src) & 1) != 0) {
3046 memcpy1 (dest, src, size);
3049 if ((((int)dest | (int)src) & 2) != 0) {
3050 memcpy2 (dest, src, size);
3054 memcpy4 (dest, src, size);
3057 internal static unsafe void CharCopy (char *dest, char *src, int count) {
3058 // Same rules as for memcpy, but with the premise that
3059 // chars can only be aligned to even addresses if their
3060 // enclosing types are correctly aligned
3061 if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
3062 if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
3063 ((short*)dest) [0] = ((short*)src) [0];
3068 if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
3069 memcpy2 ((byte*)dest, (byte*)src, count * 2);
3073 memcpy4 ((byte*)dest, (byte*)src, count * 2);
3076 internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
3080 for (int i = count; i > 0; i--) {
3087 internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
3089 fixed (char* dest = target, src = source)
3090 CharCopy (dest + targetIndex, src + sourceIndex, count);
3093 internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
3095 fixed (char* dest = target, src = source)
3096 CharCopy (dest + targetIndex, src + sourceIndex, count);
3099 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3100 internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
3102 fixed (char* dest = target, src = source)
3103 CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
3106 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3107 unsafe public extern String (char *value);
3109 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3110 unsafe public extern String (char *value, int startIndex, int length);
3112 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3113 unsafe public extern String (sbyte *value);
3115 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3116 unsafe public extern String (sbyte *value, int startIndex, int length);
3118 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
3119 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
3121 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3122 public extern String (char [] value, int startIndex, int length);
3124 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3125 public extern String (char [] value);
3127 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3128 public extern String (char c, int count);
3130 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
3131 //private extern String[] InternalSplit (char[] separator, int count, int options);
3133 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3134 internal extern static String InternalAllocateStr (int length);
3136 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3137 private extern static string InternalIntern (string str);
3139 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3140 private extern static string InternalIsInterned (string str);
3142 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3143 private extern static int GetLOSLimit ();