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)
11 // (C) 2001 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.Globalization;
37 using System.Runtime.CompilerServices;
40 using System.Collections.Generic;
41 using System.Runtime.ConstrainedExecution;
42 using System.Runtime.InteropServices;
43 using Mono.Globalization.Unicode;
51 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
53 public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable
56 [NonSerialized] private int length;
57 [NonSerialized] private char start_char;
59 private const int COMPARE_CASE = 0;
60 private const int COMPARE_INCASE = 1;
61 private const int COMPARE_ORDINAL = 2;
63 public static readonly String Empty = "";
65 public static unsafe bool Equals (string a, string b)
67 if ((a as object) == (b as object))
70 if (a == null || b == null)
78 fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
83 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
84 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
85 ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
86 ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
95 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
96 ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
105 if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
113 return len == 0 || *s1_ptr == *s2_ptr;
117 public static bool operator == (String a, String b)
119 return Equals (a, b);
122 public static bool operator != (String a, String b)
124 return !Equals (a, b);
128 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
130 public override bool Equals (Object obj)
132 return Equals (this, obj as String);
136 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
138 public bool Equals (String value)
140 return Equals (this, value);
143 [IndexerName ("Chars")]
144 public extern char this [int index] {
145 [MethodImplAttribute (MethodImplOptions.InternalCall)]
149 public Object Clone ()
154 public TypeCode GetTypeCode ()
156 return TypeCode.String;
159 public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
161 // LAMESPEC: should I null-terminate?
162 if (destination == null)
163 throw new ArgumentNullException ("destination");
165 if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
166 throw new ArgumentOutOfRangeException ();
168 // re-ordered to avoid possible integer overflow
169 if (sourceIndex > Length - count)
170 throw new ArgumentOutOfRangeException ("sourceIndex + count > Length");
171 // re-ordered to avoid possible integer overflow
172 if (destinationIndex > destination.Length - count)
173 throw new ArgumentOutOfRangeException ("destinationIndex + count > destination.Length");
175 InternalCopyTo (sourceIndex, destination, destinationIndex, count);
178 public char[] ToCharArray ()
180 return ToCharArray (0, length);
183 public char[] ToCharArray (int startIndex, int length)
186 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
188 throw new ArgumentOutOfRangeException ("length", "< 0");
189 // re-ordered to avoid possible integer overflow
190 if (startIndex > this.length - length)
191 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
193 char[] tmp = new char [length];
195 InternalCopyTo (startIndex, tmp, 0, length);
200 public String [] Split (params char [] separator)
202 return Split (separator, Int32.MaxValue);
205 public String[] Split (char[] separator, int count)
207 if (separator == null || separator.Length == 0)
208 separator = WhiteChars;
211 throw new ArgumentOutOfRangeException ("count");
214 return new String[0];
217 return new String[1] { ToString() };
219 return InternalSplit (separator, count);
225 public String[] Split (char[] separator, int count, StringSplitOptions options)
227 if (separator == null || separator.Length == 0)
228 return Split (WhiteChars, count, options);
231 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
232 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
233 throw new ArgumentException ("options must be one of the values in the StringSplitOptions enumeration", "options");
235 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
238 return Split (separator, count);
240 /* FIXME: Optimize this */
241 String[] res = Split (separator, count);
243 for (int i = 0; i < res.Length; ++i)
244 if (res [i] == String.Empty)
247 String[] arr = new String [res.Length - n];
249 for (int i = 0; i < res.Length; ++i)
250 if (res [i] != String.Empty)
251 arr [pos ++] = res [i];
260 public String[] Split (string[] separator, int count, StringSplitOptions options)
262 if (separator == null || separator.Length == 0)
263 return Split (WhiteChars, count, options);
266 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
267 if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
268 throw new ArgumentException ("Illegal enum value: " + options + ".", "options");
270 bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) == StringSplitOptions.RemoveEmptyEntries;
272 if (count == 0 || (this == String.Empty && removeEmpty))
273 return new String [0];
275 ArrayList arr = new ArrayList ();
278 while (pos < this.Length) {
280 int matchPos = Int32.MaxValue;
282 // Find the first position where any of the separators matches
283 for (int i = 0; i < separator.Length; ++i) {
284 string sep = separator [i];
285 if (sep == null || sep == String.Empty)
288 int match = IndexOf (sep, pos);
289 if (match > -1 && match < matchPos) {
295 if (matchIndex == -1)
298 if (matchPos == pos && removeEmpty) {
299 pos = matchPos + separator [matchIndex].Length;
302 arr.Add (this.Substring (pos, matchPos - pos));
304 pos = matchPos + separator [matchIndex].Length;
306 if (arr.Count == count - 1) {
313 return new String [] { this };
315 if (removeEmpty && pos == this.Length) {
316 String[] res = new String [arr.Count];
317 arr.CopyTo (0, res, 0, arr.Count);
322 String[] res = new String [arr.Count + 1];
323 arr.CopyTo (0, res, 0, arr.Count);
324 res [arr.Count] = this.Substring (pos);
332 public String[] Split (char[] separator, StringSplitOptions options)
334 return Split (separator, Int32.MaxValue, options);
338 public String[] Split (String[] separator, StringSplitOptions options)
340 return Split (separator, Int32.MaxValue, options);
344 public unsafe String Substring (int startIndex)
346 if (startIndex < 0 || startIndex > this.length)
347 throw new ArgumentOutOfRangeException ("startIndex");
349 int newlen = this.length - startIndex;
350 string tmp = InternalAllocateStr (newlen);
352 fixed (char *dest = tmp, src = this) {
353 memcpy ((byte*)dest, (byte*)(src + startIndex), newlen * 2);
359 public unsafe String Substring (int startIndex, int length)
362 throw new ArgumentOutOfRangeException ("length", "< 0");
364 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
365 // re-ordered to avoid possible integer overflow
366 if (startIndex > this.length - length)
367 throw new ArgumentOutOfRangeException ("startIndex + length > this.length");
372 string tmp = InternalAllocateStr (length);
373 fixed (char *dest = tmp, src = this) {
374 memcpy ((byte*)dest, (byte*)(src + startIndex), length * 2);
380 private static readonly char[] WhiteChars = { (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
382 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
384 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
385 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
386 (char) 0x3000, (char) 0xFEFF };
388 public String Trim ()
390 return InternalTrim (WhiteChars, 0);
393 public String Trim (params char[] trimChars)
395 if (trimChars == null || trimChars.Length == 0)
396 trimChars = WhiteChars;
398 return InternalTrim (trimChars, 0);
401 public String TrimStart (params char[] trimChars)
403 if (trimChars == null || trimChars.Length == 0)
404 trimChars = WhiteChars;
406 return InternalTrim (trimChars, 1);
409 public String TrimEnd (params char[] trimChars)
411 if (trimChars == null || trimChars.Length == 0)
412 trimChars = WhiteChars;
414 return InternalTrim (trimChars, 2);
417 public static int Compare (String strA, String strB)
419 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
422 public static int Compare (String strA, String strB, bool ignoreCase)
424 return Compare (strA, strB, ignoreCase, CultureInfo.CurrentCulture);
427 public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
430 throw new ArgumentNullException ("culture");
439 else if (strB == null) {
443 CompareOptions compopts;
446 compopts = CompareOptions.IgnoreCase;
448 compopts = CompareOptions.None;
450 return culture.CompareInfo.Compare (strA, strB, compopts);
453 public static int Compare (String strA, int indexA, String strB, int indexB, int length)
455 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
458 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
460 return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
463 public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
466 throw new ArgumentNullException ("culture");
468 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
469 throw new ArgumentOutOfRangeException ();
481 else if (strB == null) {
485 CompareOptions compopts;
488 compopts = CompareOptions.IgnoreCase;
490 compopts = CompareOptions.None;
492 /* Need to cap the requested length to the
493 * length of the string, because
494 * CompareInfo.Compare will insist that length
495 * <= (string.Length - offset)
500 if (length > (strA.Length - indexA)) {
501 len1 = strA.Length - indexA;
504 if (length > (strB.Length - indexB)) {
505 len2 = strB.Length - indexB;
508 return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
511 public static int Compare (string strA, string strB, StringComparison comparisonType)
513 switch (comparisonType) {
514 case StringComparison.CurrentCulture:
515 return Compare (strA, strB, false, CultureInfo.CurrentCulture);
516 case StringComparison.CurrentCultureIgnoreCase:
517 return Compare (strA, strB, true, CultureInfo.CurrentCulture);
518 case StringComparison.InvariantCulture:
519 return Compare (strA, strB, false, CultureInfo.InvariantCulture);
520 case StringComparison.InvariantCultureIgnoreCase:
521 return Compare (strA, strB, true, CultureInfo.InvariantCulture);
522 case StringComparison.Ordinal:
523 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
524 case StringComparison.OrdinalIgnoreCase:
525 return CompareOrdinal (strA, strB, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
527 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
528 throw new ArgumentException ("comparisonType", msg);
532 public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
534 switch (comparisonType) {
535 case StringComparison.CurrentCulture:
536 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
537 case StringComparison.CurrentCultureIgnoreCase:
538 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
539 case StringComparison.InvariantCulture:
540 return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
541 case StringComparison.InvariantCultureIgnoreCase:
542 return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
543 case StringComparison.Ordinal:
544 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
545 case StringComparison.OrdinalIgnoreCase:
546 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal | CompareOptions.IgnoreCase);
548 string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
549 throw new ArgumentException ("comparisonType", msg);
553 public static bool Equals (string a, string b, StringComparison comparisonType)
555 return String.Compare (a, b, comparisonType) == 0;
558 public bool Equals (string value, StringComparison comparisonType)
560 return String.Equals (this, value, comparisonType);
563 public int CompareTo (Object value)
568 if (!(value is String))
569 throw new ArgumentException ();
571 return String.Compare (this, (String) value, false);
574 public int CompareTo (String strB)
579 return Compare (this, strB, false);
582 public static int CompareOrdinal (String strA, String strB)
584 return CompareOrdinal (strA, strB, CompareOptions.Ordinal);
587 internal static int CompareOrdinal (String strA, String strB, CompareOptions options)
595 else if (strB == null) {
599 /* Invariant, because that is cheaper to
600 * instantiate (and chances are it already has
603 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, strB, options);
606 public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
608 return CompareOrdinal (strA, indexA, strB, indexB, length, CompareOptions.Ordinal);
611 internal static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length, CompareOptions options)
613 if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
614 throw new ArgumentOutOfRangeException ();
622 else if (strB == null) {
626 /* Need to cap the requested length to the
627 * length of the string, because
628 * CompareInfo.Compare will insist that length
629 * <= (string.Length - offset)
634 if (length > (strA.Length - indexA)) {
635 len1 = strA.Length - indexA;
638 if (length > (strB.Length - indexB)) {
639 len2 = strB.Length - indexB;
642 return CultureInfo.InvariantCulture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
645 public bool EndsWith (String value)
647 return EndsWith (value, false, CultureInfo.CurrentCulture);
655 bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
657 return (culture.CompareInfo.IsSuffix (this, value,
658 ignoreCase ? CompareOptions.IgnoreCase :
659 CompareOptions.None));
662 public int IndexOfAny (char [] anyOf)
665 throw new ArgumentNullException ("anyOf");
667 return InternalIndexOfAny (anyOf, 0, this.length);
670 public int IndexOfAny (char [] anyOf, int startIndex)
673 throw new ArgumentNullException ("anyOf");
674 if (startIndex < 0 || startIndex > this.length)
675 throw new ArgumentOutOfRangeException ("startIndex");
677 return InternalIndexOfAny (anyOf, startIndex, this.length - startIndex);
680 public int IndexOfAny (char [] anyOf, int startIndex, int count)
683 throw new ArgumentNullException ("anyOf");
685 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
687 throw new ArgumentOutOfRangeException ("count", "< 0");
688 // re-ordered to avoid possible integer overflow
689 if (startIndex > this.length - count)
690 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
692 return InternalIndexOfAny (anyOf, startIndex, count);
696 public int IndexOf (string value, StringComparison comparison)
698 return IndexOf (value, 0, value.Length, comparison);
701 public int IndexOf (string value, int startIndex, StringComparison comparison)
703 return IndexOf (value, startIndex, value.Length - startIndex, comparison);
706 public int IndexOf (string value, int startIndex, int count, StringComparison comparison)
708 switch (comparison) {
709 case StringComparison.CurrentCulture:
710 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
711 case StringComparison.CurrentCultureIgnoreCase:
712 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
713 case StringComparison.InvariantCulture:
714 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
715 case StringComparison.InvariantCultureIgnoreCase:
716 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
717 case StringComparison.Ordinal:
718 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
719 case StringComparison.OrdinalIgnoreCase:
720 return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
722 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
725 public int LastIndexOf (string value, StringComparison comparison)
727 return LastIndexOf (value, value.Length - 1, value.Length, comparison);
730 public int LastIndexOf (string value, int startIndex, StringComparison comparison)
732 return LastIndexOf (value, startIndex, startIndex + 1, comparison);
735 public int LastIndexOf (string value, int startIndex, int count, StringComparison comparison)
737 switch (comparison) {
738 case StringComparison.CurrentCulture:
739 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
740 case StringComparison.CurrentCultureIgnoreCase:
741 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
742 case StringComparison.InvariantCulture:
743 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
744 case StringComparison.InvariantCultureIgnoreCase:
745 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
746 case StringComparison.Ordinal:
747 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.Ordinal);
748 case StringComparison.OrdinalIgnoreCase:
749 return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
751 throw new SystemException ("INTERNAL ERROR: should not reach here ...");
755 public int IndexOf (char value)
757 return IndexOf (value, 0, this.length);
760 public int IndexOf (String value)
762 return IndexOf (value, 0, this.length);
765 public int IndexOf (char value, int startIndex)
767 return IndexOf (value, startIndex, this.length - startIndex);
770 public int IndexOf (String value, int startIndex)
772 return IndexOf (value, startIndex, this.length - startIndex);
775 /* This method is culture-insensitive */
776 public int IndexOf (char value, int startIndex, int count)
779 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
781 throw new ArgumentOutOfRangeException ("count", "< 0");
782 // re-ordered to avoid possible integer overflow
783 if (startIndex > this.length - count)
784 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
786 if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
789 for (int pos = startIndex; pos < startIndex + count; pos++) {
790 if (this[pos] == value)
796 /* But this one is culture-sensitive */
797 public int IndexOf (String value, int startIndex, int count)
800 throw new ArgumentNullException ("value");
802 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
804 throw new ArgumentOutOfRangeException ("count", "< 0");
805 // re-ordered to avoid possible integer overflow
806 if (startIndex > this.length - count)
807 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
809 if (value.length == 0)
812 if (startIndex == 0 && this.length == 0)
818 return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
821 public int LastIndexOfAny (char [] anyOf)
824 throw new ArgumentNullException ("anyOf");
826 return InternalLastIndexOfAny (anyOf, this.length - 1, this.length);
829 public int LastIndexOfAny (char [] anyOf, int startIndex)
832 throw new ArgumentNullException ("anyOf");
834 if (startIndex < 0 || startIndex >= this.length)
835 throw new ArgumentOutOfRangeException ();
837 if (this.length == 0)
840 return InternalLastIndexOfAny (anyOf, startIndex, startIndex + 1);
843 public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
846 throw new ArgumentNullException ("anyOf");
848 if ((startIndex < 0) || (startIndex >= this.Length))
849 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
850 if ((count < 0) || (count > this.Length))
851 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
852 if (startIndex - count + 1 < 0)
853 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
855 if (this.length == 0)
858 return InternalLastIndexOfAny (anyOf, startIndex, count);
861 public int LastIndexOf (char value)
863 if (this.length == 0)
866 return LastIndexOf (value, this.length - 1, this.length);
869 public int LastIndexOf (String value)
871 if (this.length == 0)
872 /* This overload does additional checking */
873 return LastIndexOf (value, 0, 0);
875 return LastIndexOf (value, this.length - 1, this.length);
878 public int LastIndexOf (char value, int startIndex)
880 return LastIndexOf (value, startIndex, startIndex + 1);
883 public int LastIndexOf (String value, int startIndex)
886 throw new ArgumentNullException ("value");
887 int max = startIndex;
888 if (max < this.Length)
890 return LastIndexOf (value, startIndex, max);
893 /* This method is culture-insensitive */
894 public int LastIndexOf (char value, int startIndex, int count)
896 if (startIndex == 0 && this.length == 0)
899 // >= for char (> for string)
900 if ((startIndex < 0) || (startIndex >= this.Length))
901 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
902 if ((count < 0) || (count > this.Length))
903 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
904 if (startIndex - count + 1 < 0)
905 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
907 for(int pos = startIndex; pos > startIndex - count; pos--) {
908 if (this [pos] == value)
914 /* But this one is culture-sensitive */
915 public int LastIndexOf (String value, int startIndex, int count)
918 throw new ArgumentNullException ("value");
919 // -1 > startIndex > for string (0 > startIndex >= for char)
920 if ((startIndex < -1) || (startIndex > this.Length))
921 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
922 if ((count < 0) || (count > this.Length))
923 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
924 if (startIndex - count + 1 < 0)
925 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
927 if (value.Length == 0)
930 if (startIndex == 0 && this.length == 0)
933 // This check is needed to match undocumented MS behaviour
934 if (this.length == 0 && value.length > 0)
940 if (startIndex == this.Length)
942 return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
946 public bool Contains (String value)
948 return IndexOf (value) != -1;
951 public static bool IsNullOrEmpty (String value)
953 return (value == null) || (value.Length == 0);
956 public string Normalize ()
958 return Normalize (NormalizationForm.FormC);
961 public string Normalize (NormalizationForm form)
965 return Normalization.Normalize (this, 0);
966 case NormalizationForm.FormD:
967 return Normalization.Normalize (this, 1);
968 case NormalizationForm.FormKC:
969 return Normalization.Normalize (this, 2);
970 case NormalizationForm.FormKD:
971 return Normalization.Normalize (this, 3);
975 public bool IsNormalized ()
977 return IsNormalized (NormalizationForm.FormC);
980 public bool IsNormalized (NormalizationForm form)
984 return Normalization.IsNormalized (this, 0);
985 case NormalizationForm.FormD:
986 return Normalization.IsNormalized (this, 1);
987 case NormalizationForm.FormKC:
988 return Normalization.IsNormalized (this, 2);
989 case NormalizationForm.FormKD:
990 return Normalization.IsNormalized (this, 3);
994 public string Remove (int startIndex)
997 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
998 if (startIndex >= this.length)
999 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1001 return Remove (startIndex, this.length - startIndex);
1005 public String PadLeft (int totalWidth)
1007 return PadLeft (totalWidth, ' ');
1010 public String PadLeft (int totalWidth, char paddingChar)
1013 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1015 if (totalWidth < this.length)
1016 return String.Copy (this);
1018 return InternalPad (totalWidth, paddingChar, false);
1021 public String PadRight (int totalWidth)
1023 return PadRight (totalWidth, ' ');
1026 public String PadRight (int totalWidth, char paddingChar)
1029 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1031 if (totalWidth < this.length)
1032 return String.Copy (this);
1034 return InternalPad (totalWidth, paddingChar, true);
1037 public bool StartsWith (String value)
1039 return StartsWith (value, false, CultureInfo.CurrentCulture);
1043 public bool StartsWith (string value, StringComparison comparisonType)
1045 switch (comparisonType) {
1046 case StringComparison.CurrentCulture:
1047 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1048 case StringComparison.CurrentCultureIgnoreCase:
1049 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1050 case StringComparison.InvariantCulture:
1051 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
1052 case StringComparison.InvariantCultureIgnoreCase:
1053 return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
1054 case StringComparison.Ordinal:
1055 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.Ordinal);
1056 case StringComparison.OrdinalIgnoreCase:
1057 return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.OrdinalIgnoreCase);
1063 public bool EndsWith (string value, StringComparison comparisonType)
1065 switch (comparisonType) {
1066 case StringComparison.CurrentCulture:
1067 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1068 case StringComparison.CurrentCultureIgnoreCase:
1069 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1070 case StringComparison.InvariantCulture:
1071 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
1072 case StringComparison.InvariantCultureIgnoreCase:
1073 return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
1074 case StringComparison.Ordinal:
1075 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
1076 case StringComparison.OrdinalIgnoreCase:
1077 return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
1090 bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
1092 if (culture == null)
1093 culture = CultureInfo.CurrentCulture;
1095 return (culture.CompareInfo.IsPrefix (this, value,
1096 ignoreCase ? CompareOptions.IgnoreCase :
1097 CompareOptions.None));
1100 /* This method is culture insensitive */
1101 public String Replace (char oldChar, char newChar)
1103 return InternalReplace (oldChar, newChar);
1106 /* This method is culture sensitive */
1107 public String Replace (String oldValue, String newValue)
1109 if (oldValue == null)
1110 throw new ArgumentNullException ("oldValue");
1112 if (oldValue.Length == 0)
1113 throw new ArgumentException ("oldValue is the empty string.");
1115 if (this.Length == 0)
1118 if (newValue == null)
1119 newValue = String.Empty;
1121 return InternalReplace (oldValue, newValue, CultureInfo.CurrentCulture.CompareInfo);
1124 public unsafe String Remove (int startIndex, int count)
1127 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1129 throw new ArgumentOutOfRangeException ("count", "< 0");
1130 // re-ordered to avoid possible integer overflow
1131 if (startIndex > this.length - count)
1132 throw new ArgumentOutOfRangeException ("startIndex + count > this.length");
1134 String tmp = InternalAllocateStr (this.length - count);
1136 fixed (char *dest = tmp, src = this) {
1138 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1139 int skip = startIndex + count;
1141 memcpy ((byte*)dst, (byte*)(src + skip), (length - skip) * 2);
1146 public String ToLower ()
1148 return ToLower (CultureInfo.CurrentCulture);
1151 public String ToLower (CultureInfo culture)
1153 if (culture == null)
1154 throw new ArgumentNullException ("culture");
1156 if (culture.LCID == 0x007F) { // Invariant
1157 return ToLowerInvariant ();
1159 return culture.TextInfo.ToLower (this);
1163 public unsafe String ToLowerInvariant ()
1165 internal unsafe String ToLowerInvariant ()
1168 string tmp = InternalAllocateStr (length);
1169 fixed (char* source = &start_char, dest = tmp) {
1171 char* destPtr = (char*)dest;
1172 char* sourcePtr = (char*)source;
1174 for (int n = 0; n < length; n++) {
1175 *destPtr = Char.ToLowerInvariant (*sourcePtr);
1183 public String ToUpper ()
1185 return ToUpper (CultureInfo.CurrentCulture);
1188 public String ToUpper (CultureInfo culture)
1190 if (culture == null)
1191 throw new ArgumentNullException ("culture");
1193 if (culture.LCID == 0x007F) { // Invariant
1194 return ToUpperInvariant ();
1196 return culture.TextInfo.ToUpper (this);
1200 public unsafe String ToUpperInvariant ()
1202 internal unsafe String ToUpperInvariant ()
1205 string tmp = InternalAllocateStr (length);
1206 fixed (char* source = &start_char, dest = tmp) {
1208 char* destPtr = (char*)dest;
1209 char* sourcePtr = (char*)source;
1211 for (int n = 0; n < length; n++) {
1212 *destPtr = Char.ToUpperInvariant (*sourcePtr);
1220 public override String ToString ()
1225 public String ToString (IFormatProvider provider)
1230 public static String Format (String format, Object arg0)
1232 return Format (null, format, new Object[] {arg0});
1235 public static String Format (String format, Object arg0, Object arg1)
1237 return Format (null, format, new Object[] {arg0, arg1});
1240 public static String Format (String format, Object arg0, Object arg1, Object arg2)
1242 return Format (null, format, new Object[] {arg0, arg1, arg2});
1245 public static string Format (string format, params object[] args)
1247 return Format (null, format, args);
1250 public static string Format (IFormatProvider provider, string format, params object[] args)
1252 StringBuilder b = new StringBuilder ();
1253 FormatHelper (b, provider, format, args);
1254 return b.ToString ();
1257 internal static void FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
1259 if (format == null || args == null)
1260 throw new ArgumentNullException ();
1264 while (ptr < format.length) {
1265 char c = format[ptr ++];
1268 result.Append (format, start, ptr - start - 1);
1270 // check for escaped open bracket
1272 if (format[ptr] == '{') {
1283 ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
1284 if (n >= args.Length)
1285 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1289 object arg = args[n];
1292 ICustomFormatter formatter = null;
1293 if (provider != null)
1294 formatter = provider.GetFormat (typeof (ICustomFormatter))
1295 as ICustomFormatter;
1298 else if (formatter != null)
1299 str = formatter.Format (arg_format, arg, provider);
1300 else if (arg is IFormattable)
1301 str = ((IFormattable)arg).ToString (arg_format, provider);
1303 str = arg.ToString ();
1305 // pad formatted string and append to result
1307 if (width > str.length) {
1308 const char padchar = ' ';
1309 int padlen = width - str.length;
1312 result.Append (str);
1313 result.Append (padchar, padlen);
1316 result.Append (padchar, padlen);
1317 result.Append (str);
1321 result.Append (str);
1325 else if (c == '}' && ptr < format.length && format[ptr] == '}') {
1326 result.Append (format, start, ptr - start - 1);
1329 else if (c == '}') {
1330 throw new FormatException ("Input string was not in a correct format.");
1334 if (start < format.length)
1335 result.Append (format, start, format.Length - start);
1338 public unsafe static String Copy (String str)
1341 throw new ArgumentNullException ("str");
1343 int length = str.length;
1345 String tmp = InternalAllocateStr (length);
1347 fixed (char *dest = tmp, src = str) {
1348 memcpy ((byte*)dest, (byte*)src, length * 2);
1354 public static String Concat (Object obj)
1357 return String.Empty;
1359 return obj.ToString ();
1362 public unsafe static String Concat (Object obj1, Object obj2)
1366 s1 = (obj1 != null) ? obj1.ToString () : null;
1367 s2 = (obj2 != null) ? obj2.ToString () : null;
1371 return String.Empty;
1374 } else if (s2 == null)
1377 String tmp = InternalAllocateStr (s1.Length + s2.Length);
1378 if (s1.Length != 0) {
1379 fixed (char *dest = tmp, src = s1) {
1380 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1383 if (s2.Length != 0) {
1384 fixed (char *dest = tmp, src = s2) {
1385 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1392 public static String Concat (Object obj1, Object obj2, Object obj3)
1398 s1 = obj1.ToString ();
1403 s2 = obj2.ToString ();
1408 s3 = obj3.ToString ();
1410 return Concat (s1, s2, s3);
1413 #if ! BOOTSTRAP_WITH_OLDLIB
1414 [CLSCompliant(false)]
1415 public static String Concat (Object obj1, Object obj2, Object obj3,
1416 Object obj4, __arglist)
1418 string s1, s2, s3, s4;
1423 s1 = obj1.ToString ();
1428 s2 = obj2.ToString ();
1433 s3 = obj3.ToString ();
1435 ArgIterator iter = new ArgIterator (__arglist);
1436 int argCount = iter.GetRemainingCount();
1438 StringBuilder sb = new StringBuilder ();
1440 sb.Append (obj4.ToString ());
1442 for (int i = 0; i < argCount; i++) {
1443 TypedReference typedRef = iter.GetNextArg ();
1444 sb.Append (TypedReference.ToObject (typedRef));
1447 s4 = sb.ToString ();
1449 return Concat (s1, s2, s3, s4);
1453 public unsafe static String Concat (String s1, String s2)
1455 if (s1 == null || s1.Length == 0) {
1456 if (s2 == null || s2.Length == 0)
1457 return String.Empty;
1461 if (s2 == null || s2.Length == 0)
1464 String tmp = InternalAllocateStr (s1.length + s2.length);
1466 if (s1.Length != 0) {
1467 fixed (char *dest = tmp, src = s1) {
1468 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1471 if (s2.Length != 0) {
1472 fixed (char *dest = tmp, src = s2) {
1473 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1480 public unsafe static String Concat (String s1, String s2, String s3)
1482 if (s1 == null || s1.Length == 0){
1483 if (s2 == null || s2.Length == 0){
1484 if (s3 == null || s3.Length == 0)
1485 return String.Empty;
1488 if (s3 == null || s3.Length == 0)
1493 if (s2 == null || s2.Length == 0){
1494 if (s3 == null || s3.Length == 0)
1499 if (s3 == null || s3.Length == 0)
1504 //return InternalConcat (s1, s2, s3);
1505 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length);
1507 if (s1.Length != 0) {
1508 fixed (char *dest = tmp, src = s1) {
1509 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1512 if (s2.Length != 0) {
1513 fixed (char *dest = tmp, src = s2) {
1514 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1517 if (s3.Length != 0) {
1518 fixed (char *dest = tmp, src = s3) {
1519 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1526 public unsafe static String Concat (String s1, String s2, String s3, String s4)
1528 if (s1 == null && s2 == null && s3 == null && s4 == null)
1529 return String.Empty;
1540 String tmp = InternalAllocateStr (s1.length + s2.length + s3.length + s4.length);
1542 if (s1.Length != 0) {
1543 fixed (char *dest = tmp, src = s1) {
1544 memcpy ((byte*)dest, (byte*)src, s1.length * 2);
1547 if (s2.Length != 0) {
1548 fixed (char *dest = tmp, src = s2) {
1549 memcpy ((byte*)(dest + s1.Length), (byte*)src, s2.length * 2);
1552 if (s3.Length != 0) {
1553 fixed (char *dest = tmp, src = s3) {
1554 memcpy ((byte*)(dest + s1.Length + s2.Length), (byte*)src, s3.length * 2);
1557 if (s4.Length != 0) {
1558 fixed (char *dest = tmp, src = s4) {
1559 memcpy ((byte*)(dest + s1.Length + s2.Length + s3.Length), (byte*)src, s4.length * 2);
1566 public static String Concat (params Object[] args)
1569 throw new ArgumentNullException ("args");
1571 int i = args.Length;
1573 return String.Empty;
1575 string [] strings = new string [i];
1578 foreach (object arg in args) {
1580 strings[i] = String.Empty;
1582 strings[i] = arg.ToString ();
1583 len += strings[i].length;
1589 return String.Empty;
1591 return InternalJoin (String.Empty, strings, 0, strings.Length);
1594 public static String Concat (params String[] values)
1597 throw new ArgumentNullException ("values");
1599 return InternalJoin (String.Empty, values, 0, values.Length);
1602 public unsafe String Insert (int startIndex, String value)
1605 throw new ArgumentNullException ("value");
1607 if (startIndex < 0 || startIndex > this.length)
1608 throw new ArgumentOutOfRangeException ();
1610 if (value.Length == 0)
1612 if (this.Length == 0)
1614 String tmp = InternalAllocateStr (this.length + value.length);
1616 fixed (char *dest = tmp, src = this, val = value) {
1618 memcpy ((byte*)dst, (byte*)src, startIndex * 2);
1620 memcpy ((byte*)dst, (byte*)val, value.length * 2);
1621 dst += value.length;
1622 memcpy ((byte*)dst, (byte*)(src + startIndex), (length - startIndex) * 2);
1628 public static string Intern (string str)
1631 throw new ArgumentNullException ("str");
1633 return InternalIntern (str);
1636 public static string IsInterned (string str)
1639 throw new ArgumentNullException ("str");
1641 return InternalIsInterned (str);
1644 public static string Join (string separator, string [] value)
1647 throw new ArgumentNullException ("value");
1649 return Join (separator, value, 0, value.Length);
1652 public static string Join (string separator, string[] value, int startIndex, int count)
1655 throw new ArgumentNullException ("value");
1657 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1659 throw new ArgumentOutOfRangeException ("count", "< 0");
1660 // re-ordered to avoid possible integer overflow
1661 if (startIndex > value.Length - count)
1662 throw new ArgumentOutOfRangeException ("startIndex + count > value.length");
1664 if (startIndex == value.Length)
1665 return String.Empty;
1666 if (separator == null)
1667 separator = String.Empty;
1669 return InternalJoin (separator, value, startIndex, count);
1672 bool IConvertible.ToBoolean (IFormatProvider provider)
1674 return Convert.ToBoolean (this, provider);
1677 byte IConvertible.ToByte (IFormatProvider provider)
1679 return Convert.ToByte (this, provider);
1682 char IConvertible.ToChar (IFormatProvider provider)
1684 return Convert.ToChar (this, provider);
1687 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1689 return Convert.ToDateTime (this, provider);
1692 decimal IConvertible.ToDecimal (IFormatProvider provider)
1694 return Convert.ToDecimal (this, provider);
1697 double IConvertible.ToDouble (IFormatProvider provider)
1699 return Convert.ToDouble (this, provider);
1702 short IConvertible.ToInt16 (IFormatProvider provider)
1704 return Convert.ToInt16 (this, provider);
1707 int IConvertible.ToInt32 (IFormatProvider provider)
1709 return Convert.ToInt32 (this, provider);
1712 long IConvertible.ToInt64 (IFormatProvider provider)
1714 return Convert.ToInt64 (this, provider);
1717 sbyte IConvertible.ToSByte (IFormatProvider provider)
1719 return Convert.ToSByte (this, provider);
1722 float IConvertible.ToSingle (IFormatProvider provider)
1724 return Convert.ToSingle (this, provider);
1727 object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1729 return Convert.ToType (this, conversionType, provider);
1732 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1734 return Convert.ToUInt16 (this, provider);
1737 uint IConvertible.ToUInt32 (IFormatProvider provider)
1739 return Convert.ToUInt32 (this, provider);
1742 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1744 return Convert.ToUInt64 (this, provider);
1753 public CharEnumerator GetEnumerator ()
1755 return new CharEnumerator (this);
1759 IEnumerator<char> IEnumerable<char>.GetEnumerator ()
1761 return GetEnumerator ();
1765 IEnumerator IEnumerable.GetEnumerator ()
1767 return new CharEnumerator (this);
1770 private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
1771 out bool left_align, out string format)
1773 // parses format specifier of form:
1779 // N = argument number (non-negative integer)
1781 n = ParseDecimal (str, ref ptr);
1783 throw new FormatException ("Input string was not in a correct format.");
1785 // M = width (non-negative integer)
1787 if (str[ptr] == ',') {
1788 // White space between ',' and number or sign.
1790 while (Char.IsWhiteSpace (str [ptr]))
1794 format = str.Substring (start, ptr - start);
1796 left_align = (str [ptr] == '-');
1800 width = ParseDecimal (str, ref ptr);
1802 throw new FormatException ("Input string was not in a correct format.");
1807 format = String.Empty;
1810 // F = argument format (string)
1812 if (str[ptr] == ':') {
1814 while (str[ptr] != '}')
1817 format += str.Substring (start, ptr - start);
1822 if (str[ptr ++] != '}')
1823 throw new FormatException ("Input string was not in a correct format.");
1825 catch (IndexOutOfRangeException) {
1826 throw new FormatException ("Input string was not in a correct format.");
1830 private static int ParseDecimal (string str, ref int ptr)
1836 if (c < '0' || '9' < c)
1839 n = n * 10 + c - '0';
1850 internal unsafe void InternalSetChar (int idx, char val)
1852 if ((uint) idx >= (uint) Length)
1853 throw new ArgumentOutOfRangeException ("idx");
1855 fixed (char * pStr = &start_char)
1861 internal unsafe void InternalSetLength (int newLength)
1863 if (newLength > length)
1864 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
1866 // zero terminate, we can pass string objects directly via pinvoke
1867 // we also zero the rest of the string, since the new GC needs to be
1868 // able to handle the changing size (it will skip the 0 bytes).
1869 fixed (char * pStr = &start_char) {
1870 char *p = pStr + newLength;
1871 char *end = pStr + length;
1881 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
1883 public unsafe override int GetHashCode ()
1885 fixed (char * c = this) {
1887 char * end = cc + length - 1;
1889 for (;cc < end; cc += 2) {
1890 h = (h << 5) - h + *cc;
1891 h = (h << 5) - h + cc [1];
1895 h = (h << 5) - h + *cc;
1900 // Certain constructors are redirected to CreateString methods with
1901 // matching argument list. The this pointer should not be used.
1903 private unsafe String CreateString (sbyte* value)
1906 return String.Empty;
1908 byte* bytes = (byte*) value;
1912 while (bytes++ [0] != 0)
1914 } catch (NullReferenceException) {
1915 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
1917 } catch (AccessViolationException) {
1918 throw new ArgumentOutOfRangeException ("value", "Value does not refer to a valid string.");
1922 return CreateString (value, 0, length, null);
1925 private unsafe String CreateString (sbyte* value, int startIndex, int length)
1927 return CreateString (value, startIndex, length, null);
1930 private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
1933 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
1935 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
1936 if (value + startIndex < value)
1937 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
1939 bool isDefaultEncoding;
1941 if (isDefaultEncoding = (enc == null)) {
1944 throw new ArgumentNullException ("value");
1947 if (value == null || length == 0)
1949 return String.Empty;
1951 enc = Encoding.Default;
1954 byte [] bytes = new byte [length];
1957 fixed (byte* bytePtr = bytes)
1959 memcpy (bytePtr, (byte*) (value + startIndex), length);
1960 } catch (NullReferenceException) {
1962 if (!isDefaultEncoding)
1966 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
1968 } catch (AccessViolationException) {
1969 if (!isDefaultEncoding)
1972 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
1976 // GetString () is called even when length == 0
1977 return enc.GetString (bytes);
1980 /* helpers used by the runtime as well as above or eslewhere in corlib */
1981 internal static unsafe void memset (byte *dest, int val, int len)
1992 val = val | (val << 8);
1993 val = val | (val << 16);
1996 int rest = (int)dest & 3;
2004 } while (rest != 0);
2007 ((int*)dest) [0] = val;
2008 ((int*)dest) [1] = val;
2009 ((int*)dest) [2] = val;
2010 ((int*)dest) [3] = val;
2015 ((int*)dest) [0] = val;
2027 static unsafe void memcpy4 (byte *dest, byte *src, int size) {
2028 /*while (size >= 32) {
2029 // using long is better than int and slower than double
2030 // FIXME: enable this only on correct alignment or on platforms
2031 // that can tolerate unaligned reads/writes of doubles
2032 ((double*)dest) [0] = ((double*)src) [0];
2033 ((double*)dest) [1] = ((double*)src) [1];
2034 ((double*)dest) [2] = ((double*)src) [2];
2035 ((double*)dest) [3] = ((double*)src) [3];
2040 while (size >= 16) {
2041 ((int*)dest) [0] = ((int*)src) [0];
2042 ((int*)dest) [1] = ((int*)src) [1];
2043 ((int*)dest) [2] = ((int*)src) [2];
2044 ((int*)dest) [3] = ((int*)src) [3];
2050 ((int*)dest) [0] = ((int*)src) [0];
2056 ((byte*)dest) [0] = ((byte*)src) [0];
2062 static unsafe void memcpy2 (byte *dest, byte *src, int size) {
2064 ((short*)dest) [0] = ((short*)src) [0];
2065 ((short*)dest) [1] = ((short*)src) [1];
2066 ((short*)dest) [2] = ((short*)src) [2];
2067 ((short*)dest) [3] = ((short*)src) [3];
2073 ((short*)dest) [0] = ((short*)src) [0];
2079 ((byte*)dest) [0] = ((byte*)src) [0];
2081 static unsafe void memcpy1 (byte *dest, byte *src, int size) {
2083 ((byte*)dest) [0] = ((byte*)src) [0];
2084 ((byte*)dest) [1] = ((byte*)src) [1];
2085 ((byte*)dest) [2] = ((byte*)src) [2];
2086 ((byte*)dest) [3] = ((byte*)src) [3];
2087 ((byte*)dest) [4] = ((byte*)src) [4];
2088 ((byte*)dest) [5] = ((byte*)src) [5];
2089 ((byte*)dest) [6] = ((byte*)src) [6];
2090 ((byte*)dest) [7] = ((byte*)src) [7];
2096 ((byte*)dest) [0] = ((byte*)src) [0];
2097 ((byte*)dest) [1] = ((byte*)src) [1];
2103 ((byte*)dest) [0] = ((byte*)src) [0];
2106 internal static unsafe void memcpy (byte *dest, byte *src, int size) {
2107 // FIXME: if pointers are not aligned, try to align them
2108 // so a faster routine can be used. Handle the case where
2109 // the pointers can't be reduced to have the same alignment
2110 // (just ignore the issue on x86?)
2111 if ((((int)dest | (int)src) & 3) != 0) {
2112 if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
2118 if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
2119 ((short*)dest) [0] = ((short*)src) [0];
2124 if ((((int)dest | (int)src) & 1) != 0) {
2125 memcpy1 (dest, src, size);
2128 if ((((int)dest | (int)src) & 2) != 0) {
2129 memcpy2 (dest, src, size);
2133 memcpy4 (dest, src, size);
2136 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2137 unsafe public extern String (char *value);
2139 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2140 unsafe public extern String (char *value, int startIndex, int length);
2142 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2143 unsafe public extern String (sbyte *value);
2145 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2146 unsafe public extern String (sbyte *value, int startIndex, int length);
2148 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
2149 unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
2151 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2152 public extern String (char [] val, int startIndex, int length);
2154 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2155 public extern String (char [] val);
2157 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2158 public extern String (char c, int count);
2160 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2161 private extern static string InternalJoin (string separator, string[] value, int sIndex, int count);
2163 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2164 private extern String InternalReplace (char oldChar, char newChar);
2166 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2167 private extern String InternalReplace (String oldValue, string newValue, CompareInfo comp);
2169 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2170 private extern void InternalCopyTo (int sIndex, char[] dest, int destIndex, int count);
2172 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2173 private extern String[] InternalSplit (char[] separator, int count);
2175 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2176 private extern String InternalTrim (char[] chars, int typ);
2178 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2179 private extern int InternalIndexOfAny (char [] arr, int sIndex, int count);
2181 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2182 private extern int InternalLastIndexOfAny (char [] anyOf, int sIndex, int count);
2184 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2185 private extern String InternalPad (int width, char chr, bool right);
2187 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2188 internal extern static String InternalAllocateStr (int length);
2190 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2191 internal extern static void InternalStrcpy (String dest, int destPos, String src);
2193 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2194 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars);
2196 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2197 internal extern static void InternalStrcpy (String dest, int destPos, String src, int sPos, int count);
2199 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2200 internal extern static void InternalStrcpy (String dest, int destPos, char[] chars, int sPos, int count);
2202 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2203 private extern static string InternalIntern (string str);
2205 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2206 private extern static string InternalIsInterned (string str);