6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
10 // (C) 2001 Ximian, Inc. http://www.ximian.com
11 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections;
35 using System.Globalization;
36 using System.Runtime.CompilerServices;
39 using System.Collections.Generic;
40 using System.Runtime.ConstrainedExecution;
41 using System.Runtime.InteropServices;
42 using Mono.Globalization.Unicode;
50 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
52 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
55 [NonSerialized] private int length;
56 [NonSerialized] private char start_char;
58 private const int COMPARE_CASE = 0;
59 private const int COMPARE_INCASE = 1;
60 private const int COMPARE_ORDINAL = 2;
62 public static readonly String Empty = "";
64 public static unsafe bool Equals (string a, string b)
66 if ((a as object) == (b as object))
69 if (a == null || b == null)
77 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
82 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
83 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
84 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
85 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
94 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
95 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
104 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
112 return len == 0 || *s1_ptr == *s2_ptr;
116 public static bool operator == (String a, String b)
118 return Equals (a, b);
121 public static bool operator != (String a, String b)
123 return !Equals (a, b);
127 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
129 public override bool Equals (Object obj)
131 return Equals (this, obj as String);
135 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
137 public bool Equals (String value)
139 return Equals (this, value);
142 [IndexerName ("Chars")]
143 public extern char this [int index] {
144 [MethodImplAttribute (MethodImplOptions.InternalCall)]
148 public Object Clone ()
153 public TypeCode GetTypeCode ()
155 return TypeCode.String;
158 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
160 // LAMESPEC: should I null-terminate?
161 if (destination == null)
162 throw new ArgumentNullException ("destination");
164 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
165 throw new ArgumentOutOfRangeException ();
167 // re-ordered to avoid possible integer overflow
168 if (sourceIndex > Length - count)
169 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
170 // re-ordered to avoid possible integer overflow
171 if (destinationIndex > destination.Length - count)
172 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
174 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
177 public char[] ToCharArray ()
179 return ToCharArray (0, length);
182 public char[] ToCharArray (int startIndex, int length)
185 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
187 throw new ArgumentOutOfRangeException ("length", "< 0");
188 // re-ordered to avoid possible integer overflow
189 if (startIndex > this.length - length)
190 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
192 char[] tmp = new char [length];
194 InternalCopyTo (startIndex, tmp, 0, length);
199 public String [] Split (params char [] separator)
201 return Split (separator, Int32.MaxValue);
204 public String[] Split (char[] separator, int count)
206 if (separator == null || separator.Length == 0)
207 separator = WhiteChars;
210 throw new ArgumentOutOfRangeException ("count");
213 return new String[0];
216 return new String[1] { ToString() };
218 return InternalSplit (separator, count);
224 public String[] Split (char[] separator, int count, StringSplitOptions options)
226 if (separator == null || separator.Length == 0)
227 return Split (WhiteChars, count, options);
230 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
231 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
232 throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
234 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
237 return Split (separator, count);
239 /* FIXME: Optimize this */
240 String[] res = Split (separator, count);
242 for (int i = 0; i < res.Length; ++i)
243 if (res [i] == String.Empty)
246 String[] arr = new String [res.Length - n];
248 for (int i = 0; i < res.Length; ++i)
249 if (res [i] != String.Empty)
250 arr [pos ++] = res [i];
259 public String[] Split (string[] separator, int count, StringSplitOptions options)
261 if (separator == null || separator.Length == 0)
262 return Split (WhiteChars, count, options);
265 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
266 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
267 throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
269 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
271 if (count == 0 || (this == String.Empty && removeEmpty))
272 return new String [0];
274 ArrayList arr = new ArrayList ();
277 while (pos < this.Length) {
279 int matchPos = Int32.MaxValue;
281 // Find the first position where any of the separators matches
282 for (int i = 0; i < separator.Length; ++i) {
283 string sep = separator [i];
284 if (sep == null || sep == String.Empty)
287 int match = IndexOf (sep, pos);
288 if (match > -1 && match < matchPos) {
294 if (matchIndex == -1)
297 if (matchPos == pos && removeEmpty) {
298 pos = matchPos + separator [matchIndex].Length;
301 arr.Add (this.Substring (pos, matchPos - pos));
303 pos = matchPos + separator [matchIndex].Length;
305 if (arr.Count == count - 1) {
312 return new String [] { this };
314 if (removeEmpty && pos == this.Length) {
315 String[] res = new String [arr.Count];
316 arr.CopyTo (0, res, 0, arr.Count);
321 String[] res = new String [arr.Count + 1];
322 arr.CopyTo (0, res, 0, arr.Count);
323 res [arr.Count] = this.Substring (pos);
331 public String[] Split (char[] separator, StringSplitOptions options)
333 return Split (separator, Int32.MaxValue, options);
337 public String[] Split (String[] separator, StringSplitOptions options)
339 return Split (separator, Int32.MaxValue, options);
343 public unsafe String Substring (int startIndex)
345 if (startIndex < 0 || startIndex > this.length)
346 throw new ArgumentOutOfRangeException ("startIndex");
348 int newlen = this.length - startIndex;
349 string tmp = InternalAllocateStr (newlen);
351 fixed (char *dest = tmp, src = this) {
352 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
358 public unsafe String Substring (int startIndex, int length)
361 throw new ArgumentOutOfRangeException ("length", "< 0");
363 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
364 // re-ordered to avoid possible integer overflow
365 if (startIndex > this.length - length)
366 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
371 string tmp = InternalAllocateStr (length);
372 fixed (char *dest = tmp, src = this) {
373 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
379 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
381 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
383 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
384 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
385 (char) 0x3000, (char) 0xFEFF };
387 public String Trim ()
389 return InternalTrim (WhiteChars, 0);
392 public String Trim (params char[] trimChars)
394 if (trimChars == null || trimChars.Length == 0)
395 trimChars = WhiteChars;
397 return InternalTrim (trimChars, 0);
400 public String TrimStart (params char[] trimChars)
402 if (trimChars == null || trimChars.Length == 0)
403 trimChars = WhiteChars;
405 return InternalTrim (trimChars, 1);
408 public String TrimEnd (params char[] trimChars)
410 if (trimChars == null || trimChars.Length == 0)
411 trimChars = WhiteChars;
413 return InternalTrim (trimChars, 2);
416 public static int Compare (String strA, String strB)
418 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
421 public static int Compare (String strA, String strB, bool ignoreCase)
423 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
426 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
429 throw new ArgumentNullException ("culture");
438 else if (strB == null) {
442 CompareOptions compopts;
445 compopts = CompareOptions.IgnoreCase;
447 compopts = CompareOptions.None;
449 return culture.CompareInfo.Compare (strA, strB, compopts);
452 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
454 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
457 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
459 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
462 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
465 throw new ArgumentNullException ("culture");
467 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
468 throw new ArgumentOutOfRangeException ();
480 else if (strB == null) {
484 CompareOptions compopts;
487 compopts = CompareOptions.IgnoreCase;
489 compopts = CompareOptions.None;
491 /* Need to cap the requested length to the
492 * length of the string, because
493 * CompareInfo.Compare will insist that length
494 * <= (string.Length - offset)
499 if (length > (strA.Length - indexA)) {
500 len1 = strA.Length - indexA;
503 if (length > (strB.Length - indexB)) {
504 len2 = strB.Length - indexB;
507 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
510 public static int Compare (string strA, string strB, StringComparison comparisonType)
512 switch (comparisonType) {
513 case StringComparison.CurrentCulture:
514 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
515 case StringComparison.CurrentCultureIgnoreCase:
516 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
517 case StringComparison.InvariantCulture:
518 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
519 case StringComparison.InvariantCultureIgnoreCase:
520 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
521 case StringComparison.Ordinal:
522 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
523 case StringComparison.OrdinalIgnoreCase:
524 return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
526 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
527 throw new ArgumentException ("comparisonType", msg);
531 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
533 switch (comparisonType) {
534 case StringComparison.CurrentCulture:
535 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
536 case StringComparison.CurrentCultureIgnoreCase:
537 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
538 case StringComparison.InvariantCulture:
539 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
540 case StringComparison.InvariantCultureIgnoreCase:
541 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
542 case StringComparison.Ordinal:
543 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
544 case StringComparison.OrdinalIgnoreCase:
545 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
547 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
548 throw new ArgumentException ("comparisonType", msg);
552 public static bool Equals (string a, string b, StringComparison comparisonType)
554 return String.Compare (a, b, comparisonType) == 0;
557 public bool Equals (string value, StringComparison comparisonType)
559 return String.Equals (this, value, comparisonType);
562 public int CompareTo (Object value)
567 if (!(value is String))
568 throw new ArgumentException ();
570 return String.Compare (this, (String) value, false);
573 public int CompareTo (String strB)
578 return Compare (this, strB, false);
581 public static int CompareOrdinal (String strA, String strB)
583 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
586 internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
594 else if (strB == null) {
598 /* Invariant, because that is cheaper to
599 * instantiate (and chances are it already has
602 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
605 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
607 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
610 internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
612 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
613 throw new ArgumentOutOfRangeException ();
621 else if (strB == null) {
625 /* Need to cap the requested length to the
626 * length of the string, because
627 * CompareInfo.Compare will insist that length
628 * <= (string.Length - offset)
633 if (length > (strA.Length - indexA)) {
634 len1 = strA.Length - indexA;
637 if (length > (strB.Length - indexB)) {
638 len2 = strB.Length - indexB;
641 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
644 public bool EndsWith (String value)
646 return EndsWith (value, false, CultureInfo.CurrentCulture);
654 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
656 return (culture.CompareInfo.IsSuffix (this, value,
657 ignoreCase ? CompareOptions.IgnoreCase :
658 CompareOptions.None));
661 public int IndexOfAny (char [] anyOf)
664 throw new ArgumentNullException ("anyOf");
666 return InternalIndexOfAny (anyOf, 0, this.length);
669 public int IndexOfAny (char [] anyOf, int startIndex)
672 throw new ArgumentNullException ("anyOf");
673 if (startIndex < 0 || startIndex > this.length)
674 throw new ArgumentOutOfRangeException ("startIndex");
676 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
679 public int IndexOfAny (char [] anyOf, int startIndex, int count)
682 throw new ArgumentNullException ("anyOf");
684 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
686 throw new ArgumentOutOfRangeException ("count", "< 0");
687 // re-ordered to avoid possible integer overflow
688 if (startIndex > this.length - count)
689 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
691 return InternalIndexOfAny (anyOf, startIndex, count);
695 public int IndexOf (string value, StringComparison comparison)
697 return IndexOf (value, 0, value.Length, comparison);
700 public int IndexOf (string value, int startIndex, StringComparison comparison)
702 return IndexOf (value, startIndex, value.Length - startIndex, comparison);
705 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
707 switch (comparison) {
708 case StringComparison.CurrentCulture:
709 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
710 case StringComparison.CurrentCultureIgnoreCase:
711 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
712 case StringComparison.InvariantCulture:
713 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
714 case StringComparison.InvariantCultureIgnoreCase:
715 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
716 case StringComparison.Ordinal:
717 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
718 case StringComparison.OrdinalIgnoreCase:
719 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
721 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
724 public int LastIndexOf (string value, StringComparison comparison)
726 return LastIndexOf (value, value.Length - 1, value.Length, comparison);
729 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
731 return LastIndexOf (value, startIndex, startIndex + 1, comparison);
734 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
736 switch (comparison) {
737 case StringComparison.CurrentCulture:
738 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
739 case StringComparison.CurrentCultureIgnoreCase:
740 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
741 case StringComparison.InvariantCulture:
742 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
743 case StringComparison.InvariantCultureIgnoreCase:
744 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
745 case StringComparison.Ordinal:
746 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
747 case StringComparison.OrdinalIgnoreCase:
748 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
750 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
754 public int IndexOf (char value)
756 return IndexOf (value, 0, this.length);
759 public int IndexOf (String value)
761 return IndexOf (value, 0, this.length);
764 public int IndexOf (char value, int startIndex)
766 return IndexOf (value, startIndex, this.length - startIndex);
769 public int IndexOf (String value, int startIndex)
771 return IndexOf (value, startIndex, this.length - startIndex);
774 /* This method is culture-insensitive */
775 public int IndexOf (char value, int startIndex, int count)
778 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
780 throw new ArgumentOutOfRangeException ("count", "< 0");
781 // re-ordered to avoid possible integer overflow
782 if (startIndex > this.length - count)
783 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
785 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
788 for (int pos = startIndex; pos < startIndex + count; pos++) {
789 if (this[pos] == value)
795 /* But this one is culture-sensitive */
796 public int IndexOf (String value, int startIndex, int count)
799 throw new ArgumentNullException ("value");
801 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
803 throw new ArgumentOutOfRangeException ("count", "< 0");
804 // re-ordered to avoid possible integer overflow
805 if (startIndex > this.length - count)
806 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
808 if (value.length == 0)
811 if (startIndex == 0 && this.length == 0)
817 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
820 public int LastIndexOfAny (char [] anyOf)
823 throw new ArgumentNullException ("anyOf");
825 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
828 public int LastIndexOfAny (char [] anyOf, int startIndex)
831 throw new ArgumentNullException ("anyOf");
833 if (startIndex < 0 || startIndex >= this.length)
834 throw new ArgumentOutOfRangeException ();
836 if (this.length == 0)
839 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
842 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
845 throw new ArgumentNullException ("anyOf");
847 if ((startIndex < 0) || (startIndex >= this.Length))
848 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
849 if ((count < 0) || (count > this.Length))
850 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
851 if (startIndex - count + 1 < 0)
852 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
854 if (this.length == 0)
857 return InternalLastIndexOfAny (anyOf, startIndex, count);
860 public int LastIndexOf (char value)
862 if (this.length == 0)
865 return LastIndexOf (value, this.length - 1, this.length);
868 public int LastIndexOf (String value)
870 if (this.length == 0)
871 /* This overload does additional checking */
872 return LastIndexOf (value, 0, 0);
874 return LastIndexOf (value, this.length - 1, this.length);
877 public int LastIndexOf (char value, int startIndex)
879 return LastIndexOf (value, startIndex, startIndex + 1);
882 public int LastIndexOf (String value, int startIndex)
885 throw new ArgumentNullException ("value");
886 int max = startIndex;
887 if (max < this.Length)
889 return LastIndexOf (value, startIndex, max);
892 /* This method is culture-insensitive */
893 public int LastIndexOf (char value, int startIndex, int count)
895 if (startIndex == 0 && this.length == 0)
898 // >= for char (> for string)
899 if ((startIndex < 0) || (startIndex >= this.Length))
900 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
901 if ((count < 0) || (count > this.Length))
902 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
903 if (startIndex - count + 1 < 0)
904 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
906 for(int pos = startIndex; pos > startIndex - count; pos--) {
907 if (this [pos] == value)
913 /* But this one is culture-sensitive */
914 public int LastIndexOf (String value, int startIndex, int count)
917 throw new ArgumentNullException ("value");
918 // -1 > startIndex > for string (0 > startIndex >= for char)
919 if ((startIndex < -1) || (startIndex > this.Length))
920 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
921 if ((count < 0) || (count > this.Length))
922 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
923 if (startIndex - count + 1 < 0)
924 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
926 if (value.Length == 0)
929 if (startIndex == 0 && this.length == 0)
932 // This check is needed to match undocumented MS behaviour
933 if (this.length == 0 && value.length > 0)
939 if (startIndex == this.Length)
941 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
945 public bool Contains (String value)
947 return IndexOf (value) != -1;
950 public static bool IsNullOrEmpty (String value)
952 return (value == null) || (value.Length == 0);
955 public string Normalize ()
957 return Normalize (NormalizationForm.FormC);
960 public string Normalize (NormalizationForm form)
964 return Normalization.Normalize (this, 0);
965 case NormalizationForm.FormD:
966 return Normalization.Normalize (this, 1);
967 case NormalizationForm.FormKC:
968 return Normalization.Normalize (this, 2);
969 case NormalizationForm.FormKD:
970 return Normalization.Normalize (this, 3);
974 public bool IsNormalized ()
976 return IsNormalized (NormalizationForm.FormC);
979 public bool IsNormalized (NormalizationForm form)
983 return Normalization.IsNormalized (this, 0);
984 case NormalizationForm.FormD:
985 return Normalization.IsNormalized (this, 1);
986 case NormalizationForm.FormKC:
987 return Normalization.IsNormalized (this, 2);
988 case NormalizationForm.FormKD:
989 return Normalization.IsNormalized (this, 3);
993 public string Remove (int startIndex)
996 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
997 if (startIndex >= this.length)
998 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1000 return Remove (startIndex, this.length - startIndex);
1004 public String PadLeft (int totalWidth)
1006 return PadLeft (totalWidth, ' ');
1009 public String PadLeft (int totalWidth, char paddingChar)
1012 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1014 if (totalWidth < this.length)
1015 return String.Copy (this);
1017 return InternalPad (totalWidth, paddingChar, false);
1020 public String PadRight (int totalWidth)
1022 return PadRight (totalWidth, ' ');
1025 public String PadRight (int totalWidth, char paddingChar)
1028 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1030 if (totalWidth < this.length)
1031 return String.Copy (this);
1033 return InternalPad (totalWidth, paddingChar, true);
1036 public bool StartsWith (String value)
1038 return StartsWith (value, false, CultureInfo.CurrentCulture);
1042 public bool StartsWith (string value, StringComparison comparisonType)
1044 switch (comparisonType) {
1045 case StringComparison.CurrentCulture:
1046 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1047 case StringComparison.CurrentCultureIgnoreCase:
1048 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1049 case StringComparison.InvariantCulture:
1050 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1051 case StringComparison.InvariantCultureIgnoreCase:
1052 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1053 case StringComparison.Ordinal:
1054 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1055 case StringComparison.OrdinalIgnoreCase:
1056 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1062 public bool EndsWith (string value, StringComparison comparisonType)
1064 switch (comparisonType) {
1065 case StringComparison.CurrentCulture:
1066 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1067 case StringComparison.CurrentCultureIgnoreCase:
1068 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1069 case StringComparison.InvariantCulture:
1070 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1071 case StringComparison.InvariantCultureIgnoreCase:
1072 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1073 case StringComparison.Ordinal:
1074 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1075 case StringComparison.OrdinalIgnoreCase:
1076 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1089 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1091 if (culture == null)
1092 culture = CultureInfo.CurrentCulture;
1094 return (culture.CompareInfo.IsPrefix (this, value,
1095 ignoreCase ? CompareOptions.IgnoreCase :
1096 CompareOptions.None));
1099 /* This method is culture insensitive */
1100 public String Replace (char oldChar, char newChar)
1102 return InternalReplace (oldChar, newChar);
1105 /* This method is culture sensitive */
1106 public String Replace (String oldValue, String newValue)
1108 if (oldValue == null)
1109 throw new ArgumentNullException ("oldValue");
1111 if (oldValue.Length == 0)
1112 throw new ArgumentException ("oldValue is the empty string.");
1114 if (this.Length == 0)
1117 if (newValue == null)
1118 newValue = String.Empty;
1120 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1123 public unsafe String Remove (int startIndex, int count)
1126 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1128 throw new ArgumentOutOfRangeException ("count", "< 0");
1129 // re-ordered to avoid possible integer overflow
1130 if (startIndex > this.length - count)
1131 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
1133 String tmp = InternalAllocateStr (this.length - count);
1135 fixed (char *dest = tmp, src = this) {
1137 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1138 int skip = startIndex + count;
1140 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
1145 public String ToLower ()
1147 return ToLower (CultureInfo.CurrentCulture);
1150 public String ToLower (CultureInfo culture)
1152 if (culture == null)
1153 throw new ArgumentNullException ("culture");
1155 if (culture.LCID == 0x007F) { // Invariant
1156 return ToLowerInvariant ();
1158 return culture.TextInfo.ToLower (this);
1162 public unsafe String ToLowerInvariant ()
1164 internal unsafe String ToLowerInvariant ()
1167 string tmp = InternalAllocateStr (length);
1168 fixed (char* source = &start_char, dest = tmp) {
1170 char* destPtr = (char*)dest;
1171 char* sourcePtr = (char*)source;
1173 for (int n = 0; n < length; n++) {
1174 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1182 public String ToUpper ()
1184 return ToUpper (CultureInfo.CurrentCulture);
1187 public String ToUpper (CultureInfo culture)
1189 if (culture == null)
1190 throw new ArgumentNullException ("culture");
1192 if (culture.LCID == 0x007F) { // Invariant
1193 return ToUpperInvariant ();
1195 return culture.TextInfo.ToUpper (this);
1199 public unsafe String ToUpperInvariant ()
1201 internal unsafe String ToUpperInvariant ()
1204 string tmp = InternalAllocateStr (length);
1205 fixed (char* source = &start_char, dest = tmp) {
1207 char* destPtr = (char*)dest;
1208 char* sourcePtr = (char*)source;
1210 for (int n = 0; n < length; n++) {
1211 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1219 public override String ToString ()
1224 public String ToString (IFormatProvider provider)
1229 public static String Format (String format, Object arg0)
1231 return Format (null, format, new Object[] {arg0});
1234 public static String Format (String format, Object arg0, Object arg1)
1236 return Format (null, format, new Object[] {arg0, arg1});
1239 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1241 return Format (null, format, new Object[] {arg0, arg1, arg2});
1244 public static string Format (string format, params object[] args)
1246 return Format (null, format, args);
1249 public static string Format (IFormatProvider provider, string format, params object[] args)
1251 StringBuilder b = new StringBuilder ();
1252 FormatHelper (b, provider, format, args);
1253 return b.ToString ();
1256 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1258 if (format == null || args == null)
1259 throw new ArgumentNullException ();
1263 while (ptr < format.length) {
1264 char c = format[ptr ++];
1267 result.Append (format, start, ptr - start - 1);
1269 // check for escaped open bracket
1271 if (format[ptr] == '{') {
1282 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1283 if (n >= args.Length)
1284 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1288 object arg = args[n];
1291 ICustomFormatter formatter = null;
1292 if (provider != null)
1293 formatter = provider.GetFormat (typeof (ICustomFormatter))
1294 as ICustomFormatter;
1297 else if (formatter != null)
1298 str = formatter.Format (arg_format, arg, provider);
1299 else if (arg is IFormattable)
1300 str = ((IFormattable)arg).ToString (arg_format, provider);
1302 str = arg.ToString ();
1304 // pad formatted string and append to result
1306 if (width > str.length) {
1307 const char padchar = ' ';
1308 int padlen = width - str.length;
1311 result.Append (str);
1312 result.Append (padchar, padlen);
1315 result.Append (padchar, padlen);
1316 result.Append (str);
1320 result.Append (str);
1324 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1325 result.Append (format, start, ptr - start - 1);
1328 else if (c == '}') {
1329 throw new FormatException ("Input string was not in a correct format.");
1333 if (start < format.length)
1334 result.Append (format, start, format.Length - start);
1337 public unsafe static String Copy (String str)
1340 throw new ArgumentNullException ("str");
1342 int length = str.length;
1344 String tmp = InternalAllocateStr (length);
1346 fixed (char *dest = tmp, src = str) {
1347 memcpy ((byte*)dest, (byte*)src, length * 2);
1353 public static String Concat (Object obj)
1356 return String.Empty;
1358 return obj.ToString ();
1361 public unsafe static String Concat (Object obj1, Object obj2)
1365 s1 = (obj1 != null) ? obj1.ToString () : null;
1366 s2 = (obj2 != null) ? obj2.ToString () : null;
1370 return String.Empty;
1373 } else if (s2 == null)
1376 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1377 if (s1.Length != 0) {
1378 fixed (char *dest = tmp, src = s1) {
1379 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1382 if (s2.Length != 0) {
1383 fixed (char *dest = tmp, src = s2) {
1384 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1391 public static String Concat (Object obj1, Object obj2, Object obj3)
1397 s1 = obj1.ToString ();
1402 s2 = obj2.ToString ();
1407 s3 = obj3.ToString ();
1409 return Concat (s1, s2, s3);
1412 #if ! BOOTSTRAP_WITH_OLDLIB
1413 [CLSCompliant(false)]
1414 public static String Concat (Object obj1, Object obj2, Object obj3,
1415 Object obj4, __arglist)
1417 string s1, s2, s3, s4;
1422 s1 = obj1.ToString ();
1427 s2 = obj2.ToString ();
1432 s3 = obj3.ToString ();
1434 ArgIterator iter = new ArgIterator (__arglist);
1435 int argCount = iter.GetRemainingCount();
1437 StringBuilder sb = new StringBuilder ();
1439 sb.Append (obj4.ToString ());
1441 for (int i = 0; i < argCount; i++) {
1442 TypedReference typedRef = iter.GetNextArg ();
1443 sb.Append (TypedReference.ToObject (typedRef));
1446 s4 = sb.ToString ();
1448 return Concat (s1, s2, s3, s4);
1452 public unsafe static String Concat (String s1, String s2)
1456 return String.Empty;
1463 String tmp = InternalAllocateStr (s1.length + s2.length);
1465 if (s1.Length != 0) {
1466 fixed (char *dest = tmp, src = s1) {
1467 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1470 if (s2.Length != 0) {
1471 fixed (char *dest = tmp, src = s2) {
1472 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1479 public unsafe static String Concat (String s1, String s2, String s3)
1484 return String.Empty;
1503 //return InternalConcat (s1, s2, s3);
1504 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1506 if (s1.Length != 0) {
1507 fixed (char *dest = tmp, src = s1) {
1508 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1511 if (s2.Length != 0) {
1512 fixed (char *dest = tmp, src = s2) {
1513 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1516 if (s3.Length != 0) {
1517 fixed (char *dest = tmp, src = s3) {
1518 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1525 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1527 if (s1 == null && s2 == null && s3 == null && s4 == null)
1528 return String.Empty;
1539 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1541 if (s1.Length != 0) {
1542 fixed (char *dest = tmp, src = s1) {
1543 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1546 if (s2.Length != 0) {
1547 fixed (char *dest = tmp, src = s2) {
1548 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1551 if (s3.Length != 0) {
1552 fixed (char *dest = tmp, src = s3) {
1553 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1556 if (s4.Length != 0) {
1557 fixed (char *dest = tmp, src = s4) {
1558 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1565 public static String Concat (params Object[] args)
1568 throw new ArgumentNullException ("args");
1570 int i = args.Length;
1572 return String.Empty;
1574 string [] strings = new string [i];
1577 foreach (object arg in args) {
1579 strings[i] = String.Empty;
1581 strings[i] = arg.ToString ();
1582 len += strings[i].length;
1588 return String.Empty;
1590 return InternalJoin (String.Empty, strings, 0, strings.Length);
1593 public static String Concat (params String[] values)
1596 throw new ArgumentNullException ("values");
1598 return InternalJoin (String.Empty, values, 0, values.Length);
1601 public unsafe String Insert (int startIndex, String value)
1604 throw new ArgumentNullException ("value");
1606 if (startIndex < 0 || startIndex > this.length)
1607 throw new ArgumentOutOfRangeException ();
1609 if (value.Length == 0)
1611 if (this.Length == 0)
1613 String tmp = InternalAllocateStr (this.length + value.length);
1615 fixed (char *dest = tmp, src = this, val = value) {
1617 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1619 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1620 dst += value.length;
1621 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1627 public static string Intern (string str)
1630 throw new ArgumentNullException ("str");
1632 return InternalIntern (str);
1635 public static string IsInterned (string str)
1638 throw new ArgumentNullException ("str");
1640 return InternalIsInterned (str);
1643 public static string Join (string separator, string [] value)
1646 throw new ArgumentNullException ("value");
1648 return Join (separator, value, 0, value.Length);
1651 public static string Join (string separator, string[] value, int startIndex, int count)
1654 throw new ArgumentNullException ("value");
1656 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1658 throw new ArgumentOutOfRangeException ("count", "< 0");
1659 // re-ordered to avoid possible integer overflow
1660 if (startIndex > value.Length - count)
1661 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1663 if (startIndex == value.Length)
1664 return String.Empty;
1665 if (separator == null)
1666 separator = String.Empty;
1668 return InternalJoin (separator, value, startIndex, count);
1671 bool IConvertible.ToBoolean (IFormatProvider provider)
1673 return Convert.ToBoolean (this, provider);
1676 byte IConvertible.ToByte (IFormatProvider provider)
1678 return Convert.ToByte (this, provider);
1681 char IConvertible.ToChar (IFormatProvider provider)
1683 return Convert.ToChar (this, provider);
1686 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1688 return Convert.ToDateTime (this, provider);
1691 decimal IConvertible.ToDecimal (IFormatProvider provider)
1693 return Convert.ToDecimal (this, provider);
1696 double IConvertible.ToDouble (IFormatProvider provider)
1698 return Convert.ToDouble (this, provider);
1701 short IConvertible.ToInt16 (IFormatProvider provider)
1703 return Convert.ToInt16 (this, provider);
1706 int IConvertible.ToInt32 (IFormatProvider provider)
1708 return Convert.ToInt32 (this, provider);
1711 long IConvertible.ToInt64 (IFormatProvider provider)
1713 return Convert.ToInt64 (this, provider);
1716 sbyte IConvertible.ToSByte (IFormatProvider provider)
1718 return Convert.ToSByte (this, provider);
1721 float IConvertible.ToSingle (IFormatProvider provider)
1723 return Convert.ToSingle (this, provider);
1726 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1728 return Convert.ToType (this, conversionType, provider);
1731 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1733 return Convert.ToUInt16 (this, provider);
1736 uint IConvertible.ToUInt32 (IFormatProvider provider)
1738 return Convert.ToUInt32 (this, provider);
1741 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1743 return Convert.ToUInt64 (this, provider);
1752 public CharEnumerator GetEnumerator ()
1754 return new CharEnumerator (this);
1758 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
1760 return GetEnumerator ();
1764 IEnumerator IEnumerable.GetEnumerator ()
1766 return new CharEnumerator (this);
1769 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1770 out bool left_align, out string format)
1772 // parses format specifier of form:
1778 // N = argument number (non-negative integer)
1780 n = ParseDecimal (str, ref ptr);
1782 throw new FormatException ("Input string was not in a correct format.");
1784 // M = width (non-negative integer)
1786 if (str[ptr] == ',') {
1787 // White space between ',' and number or sign.
1789 while (Char.IsWhiteSpace (str [ptr]))
1793 format = str.Substring (start, ptr - start);
1795 left_align = (str [ptr] == '-');
1799 width = ParseDecimal (str, ref ptr);
1801 throw new FormatException ("Input string was not in a correct format.");
1806 format = String.Empty;
1809 // F = argument format (string)
1811 if (str[ptr] == ':') {
1813 while (str[ptr] != '}')
1816 format += str.Substring (start, ptr - start);
1821 if (str[ptr ++] != '}')
1822 throw new FormatException ("Input string was not in a correct format.");
1824 catch (IndexOutOfRangeException) {
1825 throw new FormatException ("Input string was not in a correct format.");
1829 private static int ParseDecimal (string str, ref int ptr)
1835 if (c < '0' || '9' < c)
1838 n = n * 10 + c - '0';
1849 internal unsafe void InternalSetChar (int idx, char val)
1851 if ((uint) idx >= (uint) Length)
1852 throw new ArgumentOutOfRangeException ("idx");
1854 fixed (char * pStr = &start_char)
1860 internal unsafe void InternalSetLength (int newLength)
1862 if (newLength > length)
1863 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1865 // zero terminate, we can pass string objects directly via pinvoke
1866 // we also zero the rest of the string, since the new GC needs to be
1867 // able to handle the changing size (it will skip the 0 bytes).
1868 fixed (char * pStr = &start_char) {
1869 char *p = pStr + newLength;
1870 char *end = pStr + length;
1880 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
1882 public unsafe override int GetHashCode ()
1884 fixed (char * c = this) {
1886 char * end = cc + length - 1;
1888 for (;cc < end; cc += 2) {
1889 h = (h << 5) - h + *cc;
1890 h = (h << 5) - h + cc [1];
1894 h = (h << 5) - h + *cc;
1899 // Certain constructors are redirected to CreateString methods with
1900 // matching argument list. The this pointer should not be used.
1902 private unsafe String CreateString (sbyte* value)
1905 return String.Empty;
1907 byte* bytes = (byte*) value;
1911 while (bytes++ [0] != 0)
1913 } catch (NullReferenceException) {
1914 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
1916 } catch (AccessViolationException) {
1917 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
1921 return CreateString (value, 0, length, null);
1924 private unsafe String CreateString (sbyte* value, int startIndex, int length)
1926 return CreateString (value, startIndex, length, null);
1929 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
1932 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
1934 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
1935 if (value + startIndex < value)
1936 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
1938 bool isDefaultEncoding;
1940 if (isDefaultEncoding = (enc == null)) {
1943 throw new ArgumentNullException ("value");
1946 if (value == null || length == 0)
1948 return String.Empty;
1950 enc = Encoding.Default;
1953 byte [] bytes = new byte [length];
1956 fixed (byte* bytePtr = bytes)
1958 memcpy (bytePtr, (byte*) (value + startIndex), length);
1959 } catch (NullReferenceException) {
1961 if (!isDefaultEncoding)
1965 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
1967 } catch (AccessViolationException) {
1968 if (!isDefaultEncoding)
1971 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
1975 // GetString () is called even when length == 0
1976 return enc.GetString (bytes);
1979 /* helpers used by the runtime as well as above or eslewhere in corlib */
1980 internal static unsafe void memset (byte *dest, int val, int len)
1991 val = val | (val << 8);
1992 val = val | (val << 16);
1995 int rest = (int)dest & 3;
2003 } while (rest != 0);
2006 ((int*)dest) [0] = val;
2007 ((int*)dest) [1] = val;
2008 ((int*)dest) [2] = val;
2009 ((int*)dest) [3] = val;
2014 ((int*)dest) [0] = val;
2026 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2027 /*while (size >= 32) {
2028 // using long is better than int and slower than double
2029 // FIXME: enable this only on correct alignment or on platforms
2030 // that can tolerate unaligned reads/writes of doubles
2031 ((double*)dest) [0] = ((double*)src) [0];
2032 ((double*)dest) [1] = ((double*)src) [1];
2033 ((double*)dest) [2] = ((double*)src) [2];
2034 ((double*)dest) [3] = ((double*)src) [3];
2039 while (size >= 16) {
2040 ((int*)dest) [0] = ((int*)src) [0];
2041 ((int*)dest) [1] = ((int*)src) [1];
2042 ((int*)dest) [2] = ((int*)src) [2];
2043 ((int*)dest) [3] = ((int*)src) [3];
2049 ((int*)dest) [0] = ((int*)src) [0];
2055 ((byte*)dest) [0] = ((byte*)src) [0];
2061 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2063 ((short*)dest) [0] = ((short*)src) [0];
2064 ((short*)dest) [1] = ((short*)src) [1];
2065 ((short*)dest) [2] = ((short*)src) [2];
2066 ((short*)dest) [3] = ((short*)src) [3];
2072 ((short*)dest) [0] = ((short*)src) [0];
2078 ((byte*)dest) [0] = ((byte*)src) [0];
2080 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2082 ((byte*)dest) [0] = ((byte*)src) [0];
2083 ((byte*)dest) [1] = ((byte*)src) [1];
2084 ((byte*)dest) [2] = ((byte*)src) [2];
2085 ((byte*)dest) [3] = ((byte*)src) [3];
2086 ((byte*)dest) [4] = ((byte*)src) [4];
2087 ((byte*)dest) [5] = ((byte*)src) [5];
2088 ((byte*)dest) [6] = ((byte*)src) [6];
2089 ((byte*)dest) [7] = ((byte*)src) [7];
2095 ((byte*)dest) [0] = ((byte*)src) [0];
2096 ((byte*)dest) [1] = ((byte*)src) [1];
2102 ((byte*)dest) [0] = ((byte*)src) [0];
2105 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2106 // FIXME: if pointers are not aligned, try to align them
2107 // so a faster routine can be used. Handle the case where
2108 // the pointers can't be reduced to have the same alignment
2109 // (just ignore the issue on x86?)
2110 if ((((int)dest | (int)src) & 3) != 0) {
2111 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2117 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2118 ((short*)dest) [0] = ((short*)src) [0];
2123 if ((((int)dest | (int)src) & 1) != 0) {
2124 memcpy1 (dest, src, size);
2127 if ((((int)dest | (int)src) & 2) != 0) {
2128 memcpy2 (dest, src, size);
2132 memcpy4 (dest, src, size);
2135 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2136 unsafe public extern String (char *value);
2138 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2139 unsafe public extern String (char *value, int startIndex, int length);
2141 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2142 unsafe public extern String (sbyte *value);
2144 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2145 unsafe public extern String (sbyte *value, int startIndex, int length);
2147 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2148 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2150 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2151 public extern String (char [] val, int startIndex, int length);
2153 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2154 public extern String (char [] val);
2156 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2157 public extern String (char c, int count);
2159 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2160 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2162 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2163 private extern String InternalReplace (char oldChar, char newChar);
2165 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2166 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2168 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2169 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2171 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2172 private extern String[] InternalSplit (char[] separator, int count);
2174 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2175 private extern String InternalTrim (char[] chars, int typ);
2177 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2178 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
2180 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2181 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2183 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2184 private extern String InternalPad (int width, char chr, bool right);
2186 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2187 internal extern static String InternalAllocateStr (int length);
2189 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2190 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2192 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2193 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2195 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2196 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2198 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2199 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2201 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2202 private extern static string InternalIntern (string str);
2204 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2205 private extern static string InternalIsInterned (string str);