6 // Jeffrey Stedfast (fejj@ximian.com)
7 // Dan Lewis (dihlewis@yahoo.co.uk)
8 // Sebastien Pouliot <sebastien@ximian.com>
9 // Marek Safar (marek.safar@seznam.cz)
10 // Andreas Nahr (Classdevelopment@A-SoftTech.com)
12 // (C) 2001 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2005 Novell (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // This class contains all implementation for culture-insensitive methods.
37 // Culture-sensitive methods are implemented in the System.Globalization or
38 // Mono.Globalization namespace.
40 // Ensure that argument checks on methods don't overflow
44 using System
.Collections
;
45 using System
.Globalization
;
46 using System
.Runtime
.CompilerServices
;
48 using System
.Collections
.Generic
;
49 using System
.Runtime
.ConstrainedExecution
;
50 using System
.Runtime
.InteropServices
;
51 using Mono
.Globalization
.Unicode
;
58 [StructLayout (LayoutKind
.Sequential
)]
59 public sealed class String
: IConvertible
, ICloneable
, IEnumerable
, IComparable
, IComparable
<String
>, IEquatable
<String
>, IEnumerable
<char>
61 [NonSerialized
] private int length
;
62 [NonSerialized
] private char start_char
;
64 public static readonly String Empty
= "";
66 internal static readonly int LOS_limit
= GetLOSLimit ();
68 public static unsafe bool Equals (string a
, string b
)
70 if ((a
as object) == (b
as object))
73 if (a
== null || b
== null)
81 fixed (char* s1
= &a
.start_char
, s2
= &b
.start_char
) {
86 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0] ||
87 ((int*)s1_ptr
)[1] != ((int*)s2_ptr
)[1] ||
88 ((int*)s1_ptr
)[2] != ((int*)s2_ptr
)[2] ||
89 ((int*)s1_ptr
)[3] != ((int*)s2_ptr
)[3])
98 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0] ||
99 ((int*)s1_ptr
)[1] != ((int*)s2_ptr
)[1])
108 if (((int*)s1_ptr
)[0] != ((int*)s2_ptr
)[0])
116 return len
== 0 || *s1_ptr
== *s2_ptr
;
120 public static bool operator == (String a
, String b
)
122 return Equals (a
, b
);
125 public static bool operator != (String a
, String b
)
127 return !Equals (a
, b
);
130 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
131 public override bool Equals (Object obj
)
133 return Equals (this, obj
as String
);
136 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
137 public bool Equals (String
value)
139 return Equals (this, value);
142 [IndexerName ("Chars")]
143 public unsafe char this [int index
] {
145 if (index
< 0 || index
>= length
)
146 throw new IndexOutOfRangeException ();
147 fixed (char* c
= &start_char
)
152 public Object
Clone ()
157 public TypeCode
GetTypeCode ()
159 return TypeCode
.String
;
162 public unsafe void CopyTo (int sourceIndex
, char[] destination
, int destinationIndex
, int count
)
164 if (destination
== null)
165 throw new ArgumentNullException ("destination");
167 throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
168 if (destinationIndex
< 0)
169 throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
171 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
172 if (sourceIndex
> Length
- count
)
173 throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
174 if (destinationIndex
> destination
.Length
- count
)
175 throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
177 fixed (char* dest
= destination
, src
= this)
178 CharCopy (dest
+ destinationIndex
, src
+ sourceIndex
, count
);
181 public char[] ToCharArray ()
183 return ToCharArray (0, length
);
186 public unsafe char[] ToCharArray (int startIndex
, int length
)
189 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
191 throw new ArgumentOutOfRangeException ("length", "< 0");
192 if (startIndex
> this.length
- length
)
193 throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
195 char[] tmp
= new char [length
];
196 fixed (char* dest
= tmp
, src
= this)
197 CharCopy (dest
, src
+ startIndex
, length
);
201 public String
[] Split (params char [] separator
)
203 return Split (separator
, int.MaxValue
, 0);
206 public String
[] Split (char[] separator
, int count
)
208 return Split (separator
, count
, 0);
212 public String
[] Split (char[] separator
, StringSplitOptions options
)
214 return Split (separator
, Int32
.MaxValue
, options
);
218 public String
[] Split (char[] separator
, int count
, StringSplitOptions options
)
221 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
222 if ((options
!= StringSplitOptions
.None
) && (options
!= StringSplitOptions
.RemoveEmptyEntries
))
223 throw new ArgumentException ("Illegal enum value: " + options
+ ".");
228 new String
[1] { this }
;
231 return SplitByCharacters (separator
, count
, options
!= 0);
235 public String
[] Split (string[] separator
, StringSplitOptions options
)
237 return Split (separator
, Int32
.MaxValue
, options
);
241 public String
[] Split (string[] separator
, int count
, StringSplitOptions options
)
244 throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
245 if ((options
!= StringSplitOptions
.None
) && (options
!= StringSplitOptions
.RemoveEmptyEntries
))
246 throw new ArgumentException ("Illegal enum value: " + options
+ ".");
251 new String
[1] { this }
;
254 bool removeEmpty
= (options
& StringSplitOptions
.RemoveEmptyEntries
) != 0;
256 if (separator
== null || separator
.Length
== 0)
257 return SplitByCharacters (null, count
, removeEmpty
);
259 if (Length
== 0 && removeEmpty
)
260 return new String
[0];
262 List
<String
> arr
= new List
<String
> ();
266 while (pos
< this.Length
) {
268 int matchPos
= Int32
.MaxValue
;
270 // Find the first position where any of the separators matches
271 for (int i
= 0; i
< separator
.Length
; ++i
) {
272 string sep
= separator
[i
];
273 if (sep
== null || sep
.Length
== 0)
276 int match
= IndexOfOrdinalUnchecked (sep
, pos
, Length
);
277 if (match
> -1 && match
< matchPos
) {
283 if (matchIndex
== -1)
286 if (!(matchPos
== pos
&& removeEmpty
)) {
287 if (arr
.Count
== count
- 1)
289 arr
.Add (this.Substring (pos
, matchPos
- pos
));
292 pos
= matchPos
+ separator
[matchIndex
].Length
;
298 return new String
[] { this }
;
300 // string contained only separators
301 if (removeEmpty
&& matchCount
!= 0 && pos
== this.Length
&& arr
.Count
== 0)
302 return new String
[0];
304 if (!(removeEmpty
&& pos
== this.Length
))
305 arr
.Add (this.Substring (pos
));
307 return arr
.ToArray ();
310 // .NET 2.0 compatibility only
311 #if !NET_4_0 && !MOONLIGHT && !MOBILE
312 static readonly char[] WhiteChars
= {
313 (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
314 (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
315 (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
316 (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
317 (char) 0x3000, (char) 0xFEFF
321 unsafe string[] SplitByCharacters (char[] sep
, int count
, bool removeEmpty
)
323 #if !NET_4_0 && !MOONLIGHT && !MOBILE
324 if (sep
== null || sep
.Length
== 0)
328 int[] split_points
= null;
329 int total_points
= 0;
332 if (sep
== null || sep
.Length
== 0) {
333 fixed (char* src
= this) {
338 if (char.IsWhiteSpace (*src_ptr
++)) {
339 if (split_points
== null) {
340 split_points
= new int[8];
341 } else if (split_points
.Length
== total_points
) {
342 Array
.Resize (ref split_points
, split_points
.Length
* 2);
345 split_points
[total_points
++] = Length
- len
;
346 if (total_points
== count
&& !removeEmpty
)
349 } while (len
-- != 0);
352 fixed (char* src
= this) {
353 fixed (char* sep_src
= sep
) {
355 char* sep_ptr_end
= sep_src
+ sep
.Length
;
358 char* sep_ptr
= sep_src
;
360 if (*sep_ptr
++ == *src_ptr
) {
361 if (split_points
== null) {
362 split_points
= new int[8];
363 } else if (split_points
.Length
== total_points
) {
364 Array
.Resize (ref split_points
, split_points
.Length
* 2);
367 split_points
[total_points
++] = Length
- len
;
368 if (total_points
== count
&& !removeEmpty
)
373 } while (sep_ptr
!= sep_ptr_end
);
376 } while (len
-- != 0);
381 if (total_points
== 0)
382 return new string[] { this }
;
384 var res
= new string[Math
.Min (total_points
, count
) + 1];
388 for (; i
< total_points
; ++i
) {
389 var start
= split_points
[i
];
390 res
[i
] = SubstringUnchecked (prev_index
, start
- prev_index
);
391 prev_index
= start
+ 1;
394 res
[i
] = SubstringUnchecked (prev_index
, Length
- prev_index
);
398 for (; i
< total_points
; ++i
) {
399 var start
= split_points
[i
];
400 length
= start
- prev_index
;
405 res
[used
++] = SubstringUnchecked (prev_index
, length
);
408 prev_index
= start
+ 1;
411 length
= Length
- prev_index
;
413 res
[used
++] = SubstringUnchecked (prev_index
, length
);
415 if (used
!= res
.Length
)
416 Array
.Resize (ref res
, used
);
422 public String
Substring (int startIndex
)
426 if (startIndex
< 0 || startIndex
> this.length
)
427 throw new ArgumentOutOfRangeException ("startIndex");
429 return SubstringUnchecked (startIndex
, this.length
- startIndex
);
432 public String
Substring (int startIndex
, int length
)
435 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
437 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
438 if (startIndex
> this.length
)
439 throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
440 if (startIndex
> this.length
- length
)
441 throw new ArgumentOutOfRangeException ("length", "startIndex + length > this.length");
442 if (startIndex
== 0 && length
== this.length
)
445 return SubstringUnchecked (startIndex
, length
);
448 // This method is used by StringBuilder.ToString() and is expected to
449 // always create a new string object (or return String.Empty).
450 internal unsafe String
SubstringUnchecked (int startIndex
, int length
)
455 string tmp
= InternalAllocateStr (length
);
456 fixed (char* dest
= tmp
, src
= this) {
457 CharCopy (dest
, src
+ startIndex
, length
);
462 public String
Trim ()
466 int start
= FindNotWhiteSpace (0, length
, 1);
471 int end
= FindNotWhiteSpace (length
- 1, start
, -1);
473 int newLength
= end
- start
+ 1;
474 if (newLength
== length
)
477 return SubstringUnchecked (start
, newLength
);
480 public String
Trim (params char[] trimChars
)
482 if (trimChars
== null || trimChars
.Length
== 0)
487 int start
= FindNotInTable (0, length
, 1, trimChars
);
492 int end
= FindNotInTable (length
- 1, start
, -1, trimChars
);
494 int newLength
= end
- start
+ 1;
495 if (newLength
== length
)
498 return SubstringUnchecked (start
, newLength
);
501 public String
TrimStart (params char[] trimChars
)
506 if (trimChars
== null || trimChars
.Length
== 0)
507 start
= FindNotWhiteSpace (0, length
, 1);
509 start
= FindNotInTable (0, length
, 1, trimChars
);
514 return SubstringUnchecked (start
, length
- start
);
517 public String
TrimEnd (params char[] trimChars
)
522 if (trimChars
== null || trimChars
.Length
== 0)
523 end
= FindNotWhiteSpace (length
- 1, -1, -1);
525 end
= FindNotInTable (length
- 1, -1, -1, trimChars
);
531 return SubstringUnchecked (0, end
);
534 unsafe int FindNotWhiteSpace (int pos
, int target
, int change
)
536 #if NET_4_0 || NET_2_1
537 fixed (char* src
= this) {
538 while (pos
!= target
) {
539 if (!char.IsWhiteSpace (src
[pos
]))
546 while (pos
!= target
) {
550 if (c
< 0x9 || c
> 0xD)
555 if (c
!= 0xA0 && c
!= 0xFEFF && c
!= 0x3000) {
556 if (c
!= 0x85 && c
!= 0x1680 && c
!= 0x2028 && c
!= 0x2029)
557 if (c
< 0x2000 || c
> 0x200B)
567 private unsafe int FindNotInTable (int pos
, int target
, int change
, char[] table
)
569 fixed (char* tablePtr
= table
, thisPtr
= this) {
570 while (pos
!= target
) {
571 char c
= thisPtr
[pos
];
573 while (x
< table
.Length
) {
574 if (c
== tablePtr
[x
])
578 if (x
== table
.Length
)
586 public static int Compare (String strA
, String strB
)
588 return CultureInfo
.CurrentCulture
.CompareInfo
.Compare (strA
, strB
, CompareOptions
.None
);
591 public static int Compare (String strA
, String strB
, bool ignoreCase
)
593 return CultureInfo
.CurrentCulture
.CompareInfo
.Compare (strA
, strB
, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
596 public static int Compare (String strA
, String strB
, bool ignoreCase
, CultureInfo culture
)
599 throw new ArgumentNullException ("culture");
601 return culture
.CompareInfo
.Compare (strA
, strB
, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
604 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
)
606 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.CurrentCulture
);
609 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
, bool ignoreCase
)
611 return Compare (strA
, indexA
, strB
, indexB
, length
, ignoreCase
, CultureInfo
.CurrentCulture
);
614 public static int Compare (String strA
, int indexA
, String strB
, int indexB
, int length
, bool ignoreCase
, CultureInfo culture
)
617 throw new ArgumentNullException ("culture");
619 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
620 throw new ArgumentOutOfRangeException ();
632 else if (strB
== null) {
636 CompareOptions compopts
;
639 compopts
= CompareOptions
.IgnoreCase
;
641 compopts
= CompareOptions
.None
;
643 // Need to cap the requested length to the
644 // length of the string, because
645 // CompareInfo.Compare will insist that length
646 // <= (string.Length - offset)
651 if (length
> (strA
.Length
- indexA
)) {
652 len1
= strA
.Length
- indexA
;
655 if (length
> (strB
.Length
- indexB
)) {
656 len2
= strB
.Length
- indexB
;
659 // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
660 return culture
.CompareInfo
.Compare (strA
, indexA
, len1
, strB
, indexB
, len2
, compopts
);
663 public static int Compare (string strA
, string strB
, StringComparison comparisonType
)
665 switch (comparisonType
) {
666 case StringComparison
.CurrentCulture
:
667 return Compare (strA
, strB
, false, CultureInfo
.CurrentCulture
);
668 case StringComparison
.CurrentCultureIgnoreCase
:
669 return Compare (strA
, strB
, true, CultureInfo
.CurrentCulture
);
670 case StringComparison
.InvariantCulture
:
671 return Compare (strA
, strB
, false, CultureInfo
.InvariantCulture
);
672 case StringComparison
.InvariantCultureIgnoreCase
:
673 return Compare (strA
, strB
, true, CultureInfo
.InvariantCulture
);
674 case StringComparison
.Ordinal
:
675 return CompareOrdinalUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
676 case StringComparison
.OrdinalIgnoreCase
:
677 return CompareOrdinalCaseInsensitiveUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
679 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
680 throw new ArgumentException (msg
, "comparisonType");
684 public static int Compare (string strA
, int indexA
, string strB
, int indexB
, int length
, StringComparison comparisonType
)
686 switch (comparisonType
) {
687 case StringComparison
.CurrentCulture
:
688 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.CurrentCulture
);
689 case StringComparison
.CurrentCultureIgnoreCase
:
690 return Compare (strA
, indexA
, strB
, indexB
, length
, true, CultureInfo
.CurrentCulture
);
691 case StringComparison
.InvariantCulture
:
692 return Compare (strA
, indexA
, strB
, indexB
, length
, false, CultureInfo
.InvariantCulture
);
693 case StringComparison
.InvariantCultureIgnoreCase
:
694 return Compare (strA
, indexA
, strB
, indexB
, length
, true, CultureInfo
.InvariantCulture
);
695 case StringComparison
.Ordinal
:
696 return CompareOrdinal (strA
, indexA
, strB
, indexB
, length
);
697 case StringComparison
.OrdinalIgnoreCase
:
698 return CompareOrdinalCaseInsensitive (strA
, indexA
, strB
, indexB
, length
);
700 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
701 throw new ArgumentException (msg
, "comparisonType");
705 public static bool Equals (string a
, string b
, StringComparison comparisonType
)
707 return String
.Compare (a
, b
, comparisonType
) == 0;
710 public bool Equals (string value, StringComparison comparisonType
)
712 return String
.Compare (value, this, comparisonType
) == 0;
715 public static int Compare (string strA
, string strB
, CultureInfo culture
, CompareOptions options
)
718 throw new ArgumentNullException ("culture");
720 return culture
.CompareInfo
.Compare (strA
, strB
, options
);
723 public static int Compare (string strA
, int indexA
, string strB
, int indexB
, int length
, CultureInfo culture
, CompareOptions options
)
726 throw new ArgumentNullException ("culture");
731 if (length
> (strA
.Length
- indexA
))
732 len1
= strA
.Length
- indexA
;
734 if (length
> (strB
.Length
- indexB
))
735 len2
= strB
.Length
- indexB
;
737 return culture
.CompareInfo
.Compare (strA
, indexA
, len1
, strB
, indexB
, len2
, options
);
740 public int CompareTo (Object
value)
745 if (!(value is String
))
746 throw new ArgumentException ();
748 return String
.Compare (this, (String
) value);
751 public int CompareTo (String strB
)
756 return Compare (this, strB
);
759 public static int CompareOrdinal (String strA
, String strB
)
761 return CompareOrdinalUnchecked (strA
, 0, Int32
.MaxValue
, strB
, 0, Int32
.MaxValue
);
764 public static int CompareOrdinal (String strA
, int indexA
, String strB
, int indexB
, int length
)
766 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
767 throw new ArgumentOutOfRangeException ();
769 return CompareOrdinalUnchecked (strA
, indexA
, length
, strB
, indexB
, length
);
772 internal static int CompareOrdinalCaseInsensitive (String strA
, int indexA
, String strB
, int indexB
, int length
)
774 if ((indexA
> strA
.Length
) || (indexB
> strB
.Length
) || (indexA
< 0) || (indexB
< 0) || (length
< 0))
775 throw new ArgumentOutOfRangeException ();
777 return CompareOrdinalCaseInsensitiveUnchecked (strA
, indexA
, length
, strB
, indexB
, length
);
780 internal static unsafe int CompareOrdinalUnchecked (String strA
, int indexA
, int lenA
, String strB
, int indexB
, int lenB
)
787 } else if (strB
== null) {
790 int lengthA
= Math
.Min (lenA
, strA
.Length
- indexA
);
791 int lengthB
= Math
.Min (lenB
, strB
.Length
- indexB
);
793 if (lengthA
== lengthB
&& Object
.ReferenceEquals (strA
, strB
))
796 fixed (char* aptr
= strA
, bptr
= strB
) {
797 char* ap
= aptr
+ indexA
;
798 char* end
= ap
+ Math
.Min (lengthA
, lengthB
);
799 char* bp
= bptr
+ indexB
;
806 return lengthA
- lengthB
;
810 internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA
, int indexA
, int lenA
, String strB
, int indexB
, int lenB
)
812 // Same as above, but checks versus uppercase characters
818 } else if (strB
== null) {
821 int lengthA
= Math
.Min (lenA
, strA
.Length
- indexA
);
822 int lengthB
= Math
.Min (lenB
, strB
.Length
- indexB
);
824 if (lengthA
== lengthB
&& Object
.ReferenceEquals (strA
, strB
))
827 fixed (char* aptr
= strA
, bptr
= strB
) {
828 char* ap
= aptr
+ indexA
;
829 char* end
= ap
+ Math
.Min (lengthA
, lengthB
);
830 char* bp
= bptr
+ indexB
;
833 char c1
= Char
.ToUpperInvariant (*ap
);
834 char c2
= Char
.ToUpperInvariant (*bp
);
841 return lengthA
- lengthB
;
845 public bool EndsWith (String
value)
848 throw new ArgumentNullException ("value");
850 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
853 public bool EndsWith (String
value, bool ignoreCase
, CultureInfo culture
)
856 throw new ArgumentNullException ("value");
858 culture
= CultureInfo
.CurrentCulture
;
860 return culture
.CompareInfo
.IsSuffix (this, value,
861 ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
864 // Following methods are culture-insensitive
865 public int IndexOfAny (char [] anyOf
)
868 throw new ArgumentNullException ();
869 if (this.length
== 0)
872 return IndexOfAnyUnchecked (anyOf
, 0, this.length
);
875 public int IndexOfAny (char [] anyOf
, int startIndex
)
878 throw new ArgumentNullException ();
879 if (startIndex
< 0 || startIndex
> this.length
)
880 throw new ArgumentOutOfRangeException ();
882 return IndexOfAnyUnchecked (anyOf
, startIndex
, this.length
- startIndex
);
885 public int IndexOfAny (char [] anyOf
, int startIndex
, int count
)
888 throw new ArgumentNullException ();
889 if (startIndex
< 0 || startIndex
> this.length
)
890 throw new ArgumentOutOfRangeException ();
891 if (count
< 0 || startIndex
> this.length
- count
)
892 throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
894 return IndexOfAnyUnchecked (anyOf
, startIndex
, count
);
897 private unsafe int IndexOfAnyUnchecked (char[] anyOf
, int startIndex
, int count
)
899 if (anyOf
.Length
== 0)
902 if (anyOf
.Length
== 1)
903 return IndexOfUnchecked (anyOf
[0], startIndex
, count
);
905 fixed (char* any
= anyOf
) {
909 char* end_any_ptr
= any
+ anyOf
.Length
;
911 while (++any_ptr
!= end_any_ptr
) {
912 if (*any_ptr
> highest
) {
917 if (*any_ptr
< lowest
)
921 fixed (char* start
= &start_char
) {
922 char* ptr
= start
+ startIndex
;
923 char* end_ptr
= ptr
+ count
;
925 while (ptr
!= end_ptr
) {
926 if (*ptr
> highest
|| *ptr
< lowest
) {
932 return (int)(ptr
- start
);
935 while (++any_ptr
!= end_any_ptr
) {
936 if (*ptr
== *any_ptr
)
937 return (int)(ptr
- start
);
948 public int IndexOf (string value, StringComparison comparisonType
)
950 return IndexOf (value, 0, this.Length
, comparisonType
);
953 public int IndexOf (string value, int startIndex
, StringComparison comparisonType
)
955 return IndexOf (value, startIndex
, this.Length
- startIndex
, comparisonType
);
958 public int IndexOf (string value, int startIndex
, int count
, StringComparison comparisonType
)
960 switch (comparisonType
) {
961 case StringComparison
.CurrentCulture
:
962 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
963 case StringComparison
.CurrentCultureIgnoreCase
:
964 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
965 case StringComparison
.InvariantCulture
:
966 return CultureInfo
.InvariantCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
967 case StringComparison
.InvariantCultureIgnoreCase
:
968 return CultureInfo
.InvariantCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
969 case StringComparison
.Ordinal
:
970 return IndexOfOrdinal (value, startIndex
, count
, CompareOptions
.Ordinal
);
971 case StringComparison
.OrdinalIgnoreCase
:
972 return IndexOfOrdinal (value, startIndex
, count
, CompareOptions
.OrdinalIgnoreCase
);
974 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
975 throw new ArgumentException (msg
, "comparisonType");
979 internal int IndexOfOrdinal (string value, int startIndex
, int count
, CompareOptions options
)
982 throw new ArgumentNullException ("value");
984 throw new ArgumentOutOfRangeException ("startIndex");
985 if (count
< 0 || (this.length
- startIndex
) < count
)
986 throw new ArgumentOutOfRangeException ("count");
988 if (options
== CompareOptions
.Ordinal
)
989 return IndexOfOrdinalUnchecked (value, startIndex
, count
);
990 return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex
, count
);
993 internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex
, int count
)
995 int valueLen
= value.Length
;
996 if (count
< valueLen
)
1001 return IndexOfUnchecked (value[0], startIndex
, count
);
1005 fixed (char* thisptr
= this, valueptr
= value) {
1006 char* ap
= thisptr
+ startIndex
;
1007 char* thisEnd
= ap
+ count
- valueLen
+ 1;
1008 while (ap
!= thisEnd
) {
1009 if (*ap
== *valueptr
) {
1010 for (int i
= 1; i
< valueLen
; i
++) {
1011 if (ap
[i
] != valueptr
[i
])
1014 return (int)(ap
- thisptr
);
1023 internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex
, int count
)
1025 int valueLen
= value.Length
;
1026 if (count
< valueLen
)
1032 fixed (char* thisptr
= this, valueptr
= value) {
1033 char* ap
= thisptr
+ startIndex
;
1034 char* thisEnd
= ap
+ count
- valueLen
+ 1;
1035 while (ap
!= thisEnd
) {
1036 for (int i
= 0; i
< valueLen
; i
++) {
1037 if (Char
.ToUpperInvariant (ap
[i
]) != Char
.ToUpperInvariant (valueptr
[i
]))
1040 return (int)(ap
- thisptr
);
1048 public int LastIndexOf (string value, StringComparison comparisonType
)
1050 if (this.Length
== 0)
1051 return value.Length
== 0 ? 0 : -1;
1053 return LastIndexOf (value, this.Length
- 1, this.Length
, comparisonType
);
1056 public int LastIndexOf (string value, int startIndex
, StringComparison comparisonType
)
1058 return LastIndexOf (value, startIndex
, startIndex
+ 1, comparisonType
);
1061 public int LastIndexOf (string value, int startIndex
, int count
, StringComparison comparisonType
)
1063 switch (comparisonType
) {
1064 case StringComparison
.CurrentCulture
:
1065 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
1066 case StringComparison
.CurrentCultureIgnoreCase
:
1067 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
1068 case StringComparison
.InvariantCulture
:
1069 return CultureInfo
.InvariantCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.None
);
1070 case StringComparison
.InvariantCultureIgnoreCase
:
1071 return CultureInfo
.InvariantCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
, CompareOptions
.IgnoreCase
);
1072 case StringComparison
.Ordinal
:
1073 return LastIndexOfOrdinal (value, startIndex
, count
, CompareOptions
.Ordinal
);
1074 case StringComparison
.OrdinalIgnoreCase
:
1075 return LastIndexOfOrdinal (value, startIndex
, count
, CompareOptions
.OrdinalIgnoreCase
);
1077 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
1078 throw new ArgumentException (msg
, "comparisonType");
1082 internal int LastIndexOfOrdinal (string value, int startIndex
, int count
, CompareOptions options
)
1085 throw new ArgumentNullException ("value");
1086 if (this.Length
== 0)
1087 return value.Length
== 0 ? 0 : -1;
1088 if (value.Length
== 0)
1089 return Math
.Min (this.Length
- 1, startIndex
);
1090 if (startIndex
< 0 || startIndex
> length
)
1091 throw new ArgumentOutOfRangeException ("startIndex");
1092 if (count
< 0 || (startIndex
< count
- 1))
1093 throw new ArgumentOutOfRangeException ("count");
1095 if (options
== CompareOptions
.Ordinal
)
1096 return LastIndexOfOrdinalUnchecked (value, startIndex
, count
);
1097 return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex
, count
);
1100 internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex
, int count
)
1102 int valueLen
= value.Length
;
1103 if (count
< valueLen
)
1106 if (valueLen
<= 1) {
1108 return LastIndexOfUnchecked (value[0], startIndex
, count
);
1112 fixed (char* thisptr
= this, valueptr
= value) {
1113 char* ap
= thisptr
+ startIndex
- valueLen
+ 1;
1114 char* thisEnd
= ap
- count
+ valueLen
- 1;
1115 while (ap
!= thisEnd
) {
1116 if (*ap
== *valueptr
) {
1117 for (int i
= 1; i
< valueLen
; i
++) {
1118 if (ap
[i
] != valueptr
[i
])
1121 return (int)(ap
- thisptr
);
1130 internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex
, int count
)
1132 int valueLen
= value.Length
;
1133 if (count
< valueLen
)
1139 fixed (char* thisptr
= this, valueptr
= value) {
1140 char* ap
= thisptr
+ startIndex
- valueLen
+ 1;
1141 char* thisEnd
= ap
- count
+ valueLen
- 1;
1142 while (ap
!= thisEnd
) {
1143 for (int i
= 0; i
< valueLen
; i
++) {
1144 if (Char
.ToUpperInvariant (ap
[i
]) != Char
.ToUpperInvariant (valueptr
[i
]))
1147 return (int)(ap
- thisptr
);
1155 // Following methods are culture-insensitive
1156 public int IndexOf (char value)
1158 if (this.length
== 0)
1161 return IndexOfUnchecked (value, 0, this.length
);
1164 public int IndexOf (char value, int startIndex
)
1167 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
1168 if (startIndex
> this.length
)
1169 throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
1171 if ((startIndex
== 0 && this.length
== 0) || (startIndex
== this.length
))
1174 return IndexOfUnchecked (value, startIndex
, this.length
- startIndex
);
1177 public int IndexOf (char value, int startIndex
, int count
)
1179 if (startIndex
< 0 || startIndex
> this.length
)
1180 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
1182 throw new ArgumentOutOfRangeException ("count", "< 0");
1183 if (startIndex
> this.length
- count
)
1184 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1186 if ((startIndex
== 0 && this.length
== 0) || (startIndex
== this.length
) || (count
== 0))
1189 return IndexOfUnchecked (value, startIndex
, count
);
1192 internal unsafe int IndexOfUnchecked (char value, int startIndex
, int count
)
1194 // It helps JIT compiler to optimize comparison
1195 int value_32
= (int)value;
1197 fixed (char* start
= &start_char
) {
1198 char* ptr
= start
+ startIndex
;
1199 char* end_ptr
= ptr
+ (count
>> 3 << 3);
1201 while (ptr
!= end_ptr
) {
1202 if (*ptr
== value_32
)
1203 return (int)(ptr
- start
);
1204 if (ptr
[1] == value_32
)
1205 return (int)(ptr
- start
+ 1);
1206 if (ptr
[2] == value_32
)
1207 return (int)(ptr
- start
+ 2);
1208 if (ptr
[3] == value_32
)
1209 return (int)(ptr
- start
+ 3);
1210 if (ptr
[4] == value_32
)
1211 return (int)(ptr
- start
+ 4);
1212 if (ptr
[5] == value_32
)
1213 return (int)(ptr
- start
+ 5);
1214 if (ptr
[6] == value_32
)
1215 return (int)(ptr
- start
+ 6);
1216 if (ptr
[7] == value_32
)
1217 return (int)(ptr
- start
+ 7);
1222 end_ptr
+= count
& 0x07;
1223 while (ptr
!= end_ptr
) {
1224 if (*ptr
== value_32
)
1225 return (int)(ptr
- start
);
1233 internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex
, int count
)
1237 int end
= startIndex
+ count
;
1238 char c
= Char
.ToUpperInvariant (value);
1239 fixed (char* s
= &start_char
) {
1240 for (int i
= startIndex
; i
< end
; i
++)
1241 if (Char
.ToUpperInvariant (s
[i
]) == c
)
1247 // Following methods are culture-sensitive
1248 public int IndexOf (String
value)
1251 throw new ArgumentNullException ("value");
1252 if (value.length
== 0)
1254 if (this.length
== 0)
1256 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, 0, length
, CompareOptions
.Ordinal
);
1259 public int IndexOf (String
value, int startIndex
)
1261 return IndexOf (value, startIndex
, this.length
- startIndex
);
1264 public int IndexOf (String
value, int startIndex
, int count
)
1267 throw new ArgumentNullException ("value");
1268 if (startIndex
< 0 || startIndex
> this.length
)
1269 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
1270 if (count
< 0 || startIndex
> this.length
- count
)
1271 throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
1273 if (value.length
== 0)
1276 if (startIndex
== 0 && this.length
== 0)
1282 return CultureInfo
.CurrentCulture
.CompareInfo
.IndexOf (this, value, startIndex
, count
);
1285 // Following methods are culture-insensitive
1286 public int LastIndexOfAny (char [] anyOf
)
1289 throw new ArgumentNullException ();
1290 if (this.length
== 0)
1293 return LastIndexOfAnyUnchecked (anyOf
, this.length
- 1, this.length
);
1296 public int LastIndexOfAny (char [] anyOf
, int startIndex
)
1299 throw new ArgumentNullException ();
1300 if (this.length
== 0)
1303 if (startIndex
< 0 || startIndex
>= this.length
)
1304 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
1306 if (this.length
== 0)
1309 return LastIndexOfAnyUnchecked (anyOf
, startIndex
, startIndex
+ 1);
1312 public int LastIndexOfAny (char [] anyOf
, int startIndex
, int count
)
1315 throw new ArgumentNullException ();
1316 if (this.length
== 0)
1319 if ((startIndex
< 0) || (startIndex
>= this.Length
))
1320 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1321 if ((count
< 0) || (count
> this.Length
))
1322 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1323 if (startIndex
- count
+ 1 < 0)
1324 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1326 if (this.length
== 0)
1329 return LastIndexOfAnyUnchecked (anyOf
, startIndex
, count
);
1332 private unsafe int LastIndexOfAnyUnchecked (char [] anyOf
, int startIndex
, int count
)
1334 if (anyOf
.Length
== 1)
1335 return LastIndexOfUnchecked (anyOf
[0], startIndex
, count
);
1337 fixed (char* start
= this, testStart
= anyOf
) {
1338 char* ptr
= start
+ startIndex
;
1339 char* ptrEnd
= ptr
- count
;
1341 char* testEnd
= testStart
+ anyOf
.Length
;
1343 while (ptr
!= ptrEnd
) {
1345 while (test
!= testEnd
) {
1347 return (int)(ptr
- start
);
1356 // Following methods are culture-insensitive
1357 public int LastIndexOf (char value)
1359 if (this.length
== 0)
1362 return LastIndexOfUnchecked (value, this.length
- 1, this.length
);
1365 public int LastIndexOf (char value, int startIndex
)
1367 return LastIndexOf (value, startIndex
, startIndex
+ 1);
1370 public int LastIndexOf (char value, int startIndex
, int count
)
1372 if (this.length
== 0)
1375 // >= for char (> for string)
1376 if ((startIndex
< 0) || (startIndex
>= this.Length
))
1377 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
1378 if ((count
< 0) || (count
> this.Length
))
1379 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1380 if (startIndex
- count
+ 1 < 0)
1381 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1383 return LastIndexOfUnchecked (value, startIndex
, count
);
1386 internal unsafe int LastIndexOfUnchecked (char value, int startIndex
, int count
)
1388 // It helps JIT compiler to optimize comparison
1389 int value_32
= (int)value;
1391 fixed (char* start
= &start_char
) {
1392 char* ptr
= start
+ startIndex
;
1393 char* end_ptr
= ptr
- (count
>> 3 << 3);
1395 while (ptr
!= end_ptr
) {
1396 if (*ptr
== value_32
)
1397 return (int)(ptr
- start
);
1398 if (ptr
[-1] == value_32
)
1399 return (int)(ptr
- start
) - 1;
1400 if (ptr
[-2] == value_32
)
1401 return (int)(ptr
- start
) - 2;
1402 if (ptr
[-3] == value_32
)
1403 return (int)(ptr
- start
) - 3;
1404 if (ptr
[-4] == value_32
)
1405 return (int)(ptr
- start
) - 4;
1406 if (ptr
[-5] == value_32
)
1407 return (int)(ptr
- start
) - 5;
1408 if (ptr
[-6] == value_32
)
1409 return (int)(ptr
- start
) - 6;
1410 if (ptr
[-7] == value_32
)
1411 return (int)(ptr
- start
) - 7;
1416 end_ptr
-= count
& 0x07;
1417 while (ptr
!= end_ptr
) {
1418 if (*ptr
== value_32
)
1419 return (int)(ptr
- start
);
1427 internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex
, int count
)
1431 int end
= startIndex
- count
;
1432 char c
= Char
.ToUpperInvariant (value);
1433 fixed (char* s
= &start_char
) {
1434 for (int i
= startIndex
; i
> end
; i
--)
1435 if (Char
.ToUpperInvariant (s
[i
]) == c
)
1441 // Following methods are culture-sensitive
1442 public int LastIndexOf (String
value)
1444 return LastIndexOf (value, this.length
- 1, this.length
);
1447 public int LastIndexOf (String
value, int startIndex
)
1449 int max
= startIndex
;
1450 if (max
< this.Length
)
1452 return LastIndexOf (value, startIndex
, max
);
1455 public int LastIndexOf (String
value, int startIndex
, int count
)
1458 throw new ArgumentNullException ("value");
1460 if (this.length
== 0)
1461 return value.Length
== 0 ? 0 : -1;
1462 // -1 > startIndex > for string (0 > startIndex >= for char)
1463 if ((startIndex
< -1) || (startIndex
> this.Length
))
1464 throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
1465 if ((count
< 0) || (count
> this.Length
))
1466 throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
1467 if (startIndex
- count
+ 1 < 0)
1468 throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
1470 if (value.Length
== 0)
1471 return Math
.Min (this.Length
- 1, startIndex
);
1473 if (startIndex
== 0 && this.length
== 0)
1476 // This check is needed to match undocumented MS behaviour
1477 if (this.length
== 0 && value.length
> 0)
1483 if (startIndex
== this.Length
)
1485 return CultureInfo
.CurrentCulture
.CompareInfo
.LastIndexOf (this, value, startIndex
, count
);
1488 public bool Contains (String
value)
1490 return IndexOf (value) != -1;
1493 public static bool IsNullOrEmpty (String
value)
1495 return (value == null) || (value.Length
== 0);
1499 public string Normalize ()
1501 return Normalization
.Normalize (this, 0);
1504 public string Normalize (NormalizationForm normalizationForm
)
1506 switch (normalizationForm
) {
1508 return Normalization
.Normalize (this, 0);
1509 case NormalizationForm
.FormD
:
1510 return Normalization
.Normalize (this, 1);
1511 case NormalizationForm
.FormKC
:
1512 return Normalization
.Normalize (this, 2);
1513 case NormalizationForm
.FormKD
:
1514 return Normalization
.Normalize (this, 3);
1518 public bool IsNormalized ()
1520 return Normalization
.IsNormalized (this, 0);
1523 public bool IsNormalized (NormalizationForm normalizationForm
)
1525 switch (normalizationForm
) {
1527 return Normalization
.IsNormalized (this, 0);
1528 case NormalizationForm
.FormD
:
1529 return Normalization
.IsNormalized (this, 1);
1530 case NormalizationForm
.FormKC
:
1531 return Normalization
.IsNormalized (this, 2);
1532 case NormalizationForm
.FormKD
:
1533 return Normalization
.IsNormalized (this, 3);
1538 public string Remove (int startIndex
)
1541 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
1542 if (startIndex
>= this.length
)
1543 throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
1545 return Remove (startIndex
, this.length
- startIndex
);
1548 public String
PadLeft (int totalWidth
)
1550 return PadLeft (totalWidth
, ' ');
1553 public unsafe String
PadLeft (int totalWidth
, char paddingChar
)
1555 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1558 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1560 if (totalWidth
< this.length
)
1562 if (totalWidth
== 0)
1565 String tmp
= InternalAllocateStr (totalWidth
);
1567 fixed (char* dest
= tmp
, src
= this) {
1568 char* padPos
= dest
;
1569 char* padTo
= dest
+ (totalWidth
- length
);
1570 while (padPos
!= padTo
)
1571 *padPos
++ = paddingChar
;
1573 CharCopy (padTo
, src
, length
);
1578 public String
PadRight (int totalWidth
)
1580 return PadRight (totalWidth
, ' ');
1583 public unsafe String
PadRight (int totalWidth
, char paddingChar
)
1585 //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
1588 throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
1590 if (totalWidth
< this.length
)
1592 if (totalWidth
== 0)
1595 String tmp
= InternalAllocateStr (totalWidth
);
1597 fixed (char* dest
= tmp
, src
= this) {
1598 CharCopy (dest
, src
, length
);
1600 char* padPos
= dest
+ length
;
1601 char* padTo
= dest
+ totalWidth
;
1602 while (padPos
!= padTo
)
1603 *padPos
++ = paddingChar
;
1608 public bool StartsWith (String
value)
1611 throw new ArgumentNullException ("value");
1613 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1616 [ComVisible (false)]
1617 public bool StartsWith (string value, StringComparison comparisonType
)
1620 throw new ArgumentNullException ("value");
1622 switch (comparisonType
) {
1623 case StringComparison
.CurrentCulture
:
1624 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1625 case StringComparison
.CurrentCultureIgnoreCase
:
1626 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.IgnoreCase
);
1627 case StringComparison
.InvariantCulture
:
1628 return CultureInfo
.InvariantCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.None
);
1629 case StringComparison
.InvariantCultureIgnoreCase
:
1630 return CultureInfo
.InvariantCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.IgnoreCase
);
1631 case StringComparison
.Ordinal
:
1632 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.Ordinal
);
1633 case StringComparison
.OrdinalIgnoreCase
:
1634 return CultureInfo
.CurrentCulture
.CompareInfo
.IsPrefix (this, value, CompareOptions
.OrdinalIgnoreCase
);
1636 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
1637 throw new ArgumentException (msg
, "comparisonType");
1641 [ComVisible (false)]
1642 public bool EndsWith (string value, StringComparison comparisonType
)
1645 throw new ArgumentNullException ("value");
1647 switch (comparisonType
) {
1648 case StringComparison
.CurrentCulture
:
1649 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
1650 case StringComparison
.CurrentCultureIgnoreCase
:
1651 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.IgnoreCase
);
1652 case StringComparison
.InvariantCulture
:
1653 return CultureInfo
.InvariantCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.None
);
1654 case StringComparison
.InvariantCultureIgnoreCase
:
1655 return CultureInfo
.InvariantCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.IgnoreCase
);
1656 case StringComparison
.Ordinal
:
1657 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.Ordinal
);
1658 case StringComparison
.OrdinalIgnoreCase
:
1659 return CultureInfo
.CurrentCulture
.CompareInfo
.IsSuffix (this, value, CompareOptions
.OrdinalIgnoreCase
);
1661 string msg
= Locale
.GetText ("Invalid value '{0}' for StringComparison", comparisonType
);
1662 throw new ArgumentException (msg
, "comparisonType");
1666 public bool StartsWith (String
value, bool ignoreCase
, CultureInfo culture
)
1668 if (culture
== null)
1669 culture
= CultureInfo
.CurrentCulture
;
1671 return culture
.CompareInfo
.IsPrefix (this, value, ignoreCase
? CompareOptions
.IgnoreCase
: CompareOptions
.None
);
1674 // Following method is culture-insensitive
1675 public unsafe String
Replace (char oldChar
, char newChar
)
1677 if (this.length
== 0 || oldChar
== newChar
)
1680 int start_pos
= IndexOfUnchecked (oldChar
, 0, this.length
);
1681 if (start_pos
== -1)
1687 string tmp
= InternalAllocateStr (length
);
1688 fixed (char* dest
= tmp
, src
= &start_char
) {
1690 CharCopy (dest
, src
, start_pos
);
1692 char* end_ptr
= dest
+ length
;
1693 char* dest_ptr
= dest
+ start_pos
;
1694 char* src_ptr
= src
+ start_pos
;
1696 while (dest_ptr
!= end_ptr
) {
1697 if (*src_ptr
== oldChar
)
1698 *dest_ptr
= newChar
;
1700 *dest_ptr
= *src_ptr
;
1709 // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
1710 public String
Replace (String oldValue
, String newValue
)
1712 // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
1713 // LAMESPEC: Result is undefined if result length is longer than maximum string length
1715 if (oldValue
== null)
1716 throw new ArgumentNullException ("oldValue");
1718 if (oldValue
.Length
== 0)
1719 throw new ArgumentException ("oldValue is the empty string.");
1721 if (this.Length
== 0)
1724 if (newValue
== null)
1727 return ReplaceUnchecked (oldValue
, newValue
);
1730 private unsafe String
ReplaceUnchecked (String oldValue
, String newValue
)
1732 if (oldValue
.length
> length
)
1734 if (oldValue
.length
== 1 && newValue
.length
== 1) {
1735 return Replace (oldValue
[0], newValue
[0]);
1736 // ENHANCE: It would be possible to special case oldValue.length == newValue.length
1737 // because the length of the result would be this.length and length calculation unneccesary
1740 const int maxValue
= 200; // Allocate 800 byte maximum
1741 int* dat
= stackalloc int[maxValue
];
1742 fixed (char* source
= this, replace
= newValue
) {
1743 int i
= 0, count
= 0;
1744 while (i
< length
) {
1745 int found
= IndexOfOrdinalUnchecked (oldValue
, i
, length
- i
);
1749 if (count
< maxValue
)
1750 dat
[count
++] = found
;
1752 return ReplaceFallback (oldValue
, newValue
, maxValue
);
1754 i
= found
+ oldValue
.length
;
1758 int nlen
= this.length
+ ((newValue
.length
- oldValue
.length
) * count
);
1759 String tmp
= InternalAllocateStr (nlen
);
1761 int curPos
= 0, lastReadPos
= 0;
1762 fixed (char* dest
= tmp
) {
1763 for (int j
= 0; j
< count
; j
++) {
1764 int precopy
= dat
[j
] - lastReadPos
;
1765 CharCopy (dest
+ curPos
, source
+ lastReadPos
, precopy
);
1767 lastReadPos
= dat
[j
] + oldValue
.length
;
1768 CharCopy (dest
+ curPos
, replace
, newValue
.length
);
1769 curPos
+= newValue
.length
;
1771 CharCopy (dest
+ curPos
, source
+ lastReadPos
, length
- lastReadPos
);
1777 private String
ReplaceFallback (String oldValue
, String newValue
, int testedCount
)
1779 int lengthEstimate
= this.length
+ ((newValue
.length
- oldValue
.length
) * testedCount
);
1780 StringBuilder sb
= new StringBuilder (lengthEstimate
);
1781 for (int i
= 0; i
< length
;) {
1782 int found
= IndexOfOrdinalUnchecked (oldValue
, i
, length
- i
);
1784 sb
.Append (SubstringUnchecked (i
, length
- i
));
1787 sb
.Append (SubstringUnchecked (i
, found
- i
));
1788 sb
.Append (newValue
);
1789 i
= found
+ oldValue
.Length
;
1791 return sb
.ToString ();
1795 public unsafe String
Remove (int startIndex
, int count
)
1798 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
1800 throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
1801 if (startIndex
> this.length
- count
)
1802 throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
1804 String tmp
= InternalAllocateStr (this.length
- count
);
1806 fixed (char *dest
= tmp
, src
= this) {
1808 CharCopy (dst
, src
, startIndex
);
1809 int skip
= startIndex
+ count
;
1811 CharCopy (dst
, src
+ skip
, length
- skip
);
1816 public String
ToLower ()
1818 return ToLower (CultureInfo
.CurrentCulture
);
1821 public String
ToLower (CultureInfo culture
)
1823 if (culture
== null)
1824 throw new ArgumentNullException ("culture");
1826 if (culture
.LCID
== 0x007F) // Invariant
1827 return ToLowerInvariant ();
1829 return culture
.TextInfo
.ToLower (this);
1832 public unsafe String
ToLowerInvariant ()
1837 string tmp
= InternalAllocateStr (length
);
1838 fixed (char* source
= &start_char
, dest
= tmp
) {
1840 char* destPtr
= (char*)dest
;
1841 char* sourcePtr
= (char*)source
;
1843 for (int n
= 0; n
< length
; n
++) {
1844 *destPtr
= Char
.ToLowerInvariant (*sourcePtr
);
1852 public String
ToUpper ()
1854 return ToUpper (CultureInfo
.CurrentCulture
);
1857 public String
ToUpper (CultureInfo culture
)
1859 if (culture
== null)
1860 throw new ArgumentNullException ("culture");
1862 if (culture
.LCID
== 0x007F) // Invariant
1863 return ToUpperInvariant ();
1865 return culture
.TextInfo
.ToUpper (this);
1868 public unsafe String
ToUpperInvariant ()
1873 string tmp
= InternalAllocateStr (length
);
1874 fixed (char* source
= &start_char
, dest
= tmp
) {
1876 char* destPtr
= (char*)dest
;
1877 char* sourcePtr
= (char*)source
;
1879 for (int n
= 0; n
< length
; n
++) {
1880 *destPtr
= Char
.ToUpperInvariant (*sourcePtr
);
1888 public override String
ToString ()
1893 public String
ToString (IFormatProvider provider
)
1898 public static String
Format (String format
, Object arg0
)
1900 return Format (null, format
, new Object
[] {arg0}
);
1903 public static String
Format (String format
, Object arg0
, Object arg1
)
1905 return Format (null, format
, new Object
[] {arg0, arg1}
);
1908 public static String
Format (String format
, Object arg0
, Object arg1
, Object arg2
)
1910 return Format (null, format
, new Object
[] {arg0, arg1, arg2}
);
1913 public static string Format (string format
, params object[] args
)
1915 return Format (null, format
, args
);
1918 public static string Format (IFormatProvider provider
, string format
, params object[] args
)
1920 StringBuilder b
= FormatHelper (null, provider
, format
, args
);
1921 return b
.ToString ();
1924 internal static StringBuilder
FormatHelper (StringBuilder result
, IFormatProvider provider
, string format
, params object[] args
)
1927 throw new ArgumentNullException ("format");
1929 throw new ArgumentNullException ("args");
1931 if (result
== null) {
1932 /* Try to approximate the size of result to avoid reallocations */
1936 for (i
= 0; i
< args
.Length
; ++i
) {
1937 string s
= args
[i
] as string;
1943 if (i
== args
.Length
)
1944 result
= new StringBuilder (len
+ format
.length
);
1946 result
= new StringBuilder ();
1951 while (ptr
< format
.length
) {
1952 char c
= format
[ptr
++];
1955 result
.Append (format
, start
, ptr
- start
- 1);
1957 // check for escaped open bracket
1959 if (format
[ptr
] == '{') {
1970 ParseFormatSpecifier (format
, ref ptr
, out n
, out width
, out left_align
, out arg_format
);
1971 if (n
>= args
.Length
)
1972 throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
1976 object arg
= args
[n
];
1979 ICustomFormatter formatter
= null;
1980 if (provider
!= null)
1981 formatter
= provider
.GetFormat (typeof (ICustomFormatter
))
1982 as ICustomFormatter
;
1985 else if (formatter
!= null)
1986 str
= formatter
.Format (arg_format
, arg
, provider
);
1987 else if (arg
is IFormattable
)
1988 str
= ((IFormattable
)arg
).ToString (arg_format
, provider
);
1990 str
= arg
.ToString ();
1992 // pad formatted string and append to result
1994 if (width
> str
.length
) {
1995 const char padchar
= ' ';
1996 int padlen
= width
- str
.length
;
1999 result
.Append (str
);
2000 result
.Append (padchar
, padlen
);
2003 result
.Append (padchar
, padlen
);
2004 result
.Append (str
);
2008 result
.Append (str
);
2012 else if (c
== '}' && ptr
< format
.length
&& format
[ptr
] == '}') {
2013 result
.Append (format
, start
, ptr
- start
- 1);
2016 else if (c
== '}') {
2017 throw new FormatException ("Input string was not in a correct format.");
2021 if (start
< format
.length
)
2022 result
.Append (format
, start
, format
.Length
- start
);
2027 public unsafe static String
Copy (String str
)
2030 throw new ArgumentNullException ("str");
2032 int length
= str
.length
;
2034 String tmp
= InternalAllocateStr (length
);
2036 fixed (char *dest
= tmp
, src
= str
) {
2037 CharCopy (dest
, src
, length
);
2043 public static String
Concat (Object arg0
)
2048 return arg0
.ToString ();
2051 public static String
Concat (Object arg0
, Object arg1
)
2053 return Concat ((arg0
!= null) ? arg0
.ToString () : null, (arg1
!= null) ? arg1
.ToString () : null);
2056 public static String
Concat (Object arg0
, Object arg1
, Object arg2
)
2062 s1
= arg0
.ToString ();
2067 s2
= arg1
.ToString ();
2072 s3
= arg2
.ToString ();
2074 return Concat (s1
, s2
, s3
);
2077 [CLSCompliant(false)]
2078 public static String
Concat (Object arg0
, Object arg1
, Object arg2
,
2079 Object arg3
, __arglist
)
2081 string s1
, s2
, s3
, s4
;
2086 s1
= arg0
.ToString ();
2091 s2
= arg1
.ToString ();
2096 s3
= arg2
.ToString ();
2098 ArgIterator iter
= new ArgIterator (__arglist
);
2099 int argCount
= iter
.GetRemainingCount();
2101 StringBuilder sb
= new StringBuilder ();
2103 sb
.Append (arg3
.ToString ());
2105 for (int i
= 0; i
< argCount
; i
++) {
2106 TypedReference typedRef
= iter
.GetNextArg ();
2107 sb
.Append (TypedReference
.ToObject (typedRef
));
2110 s4
= sb
.ToString ();
2112 return Concat (s1
, s2
, s3
, s4
);
2115 public unsafe static String
Concat (String str0
, String str1
)
2117 if (str0
== null || str0
.Length
== 0) {
2118 if (str1
== null || str1
.Length
== 0)
2123 if (str1
== null || str1
.Length
== 0)
2126 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
);
2128 fixed (char *dest
= tmp
, src
= str0
)
2129 CharCopy (dest
, src
, str0
.length
);
2130 fixed (char *dest
= tmp
, src
= str1
)
2131 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2136 public unsafe static String
Concat (String str0
, String str1
, String str2
)
2138 if (str0
== null || str0
.Length
== 0){
2139 if (str1
== null || str1
.Length
== 0){
2140 if (str2
== null || str2
.Length
== 0)
2144 if (str2
== null || str2
.Length
== 0)
2149 if (str1
== null || str1
.Length
== 0){
2150 if (str2
== null || str2
.Length
== 0)
2155 if (str2
== null || str2
.Length
== 0)
2160 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
+ str2
.length
);
2162 if (str0
.Length
!= 0) {
2163 fixed (char *dest
= tmp
, src
= str0
) {
2164 CharCopy (dest
, src
, str0
.length
);
2167 if (str1
.Length
!= 0) {
2168 fixed (char *dest
= tmp
, src
= str1
) {
2169 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2172 if (str2
.Length
!= 0) {
2173 fixed (char *dest
= tmp
, src
= str2
) {
2174 CharCopy (dest
+ str0
.Length
+ str1
.Length
, src
, str2
.length
);
2181 public unsafe static String
Concat (String str0
, String str1
, String str2
, String str3
)
2183 if (str0
== null && str1
== null && str2
== null && str3
== null)
2195 String tmp
= InternalAllocateStr (str0
.length
+ str1
.length
+ str2
.length
+ str3
.length
);
2197 if (str0
.Length
!= 0) {
2198 fixed (char *dest
= tmp
, src
= str0
) {
2199 CharCopy (dest
, src
, str0
.length
);
2202 if (str1
.Length
!= 0) {
2203 fixed (char *dest
= tmp
, src
= str1
) {
2204 CharCopy (dest
+ str0
.Length
, src
, str1
.length
);
2207 if (str2
.Length
!= 0) {
2208 fixed (char *dest
= tmp
, src
= str2
) {
2209 CharCopy (dest
+ str0
.Length
+ str1
.Length
, src
, str2
.length
);
2212 if (str3
.Length
!= 0) {
2213 fixed (char *dest
= tmp
, src
= str3
) {
2214 CharCopy (dest
+ str0
.Length
+ str1
.Length
+ str2
.Length
, src
, str3
.length
);
2221 public static String
Concat (params Object
[] args
)
2224 throw new ArgumentNullException ("args");
2226 int argLen
= args
.Length
;
2230 string [] strings
= new string [argLen
];
2232 for (int i
= 0; i
< argLen
; i
++) {
2233 if (args
[i
] != null) {
2234 strings
[i
] = args
[i
].ToString ();
2235 len
+= strings
[i
].length
;
2239 return ConcatInternal (strings
, len
);
2242 public static String
Concat (params String
[] values
)
2245 throw new ArgumentNullException ("values");
2248 for (int i
= 0; i
< values
.Length
; i
++) {
2249 String s
= values
[i
];
2254 return ConcatInternal (values
, len
);
2257 private static unsafe String
ConcatInternal (String
[] values
, int length
)
2262 String tmp
= InternalAllocateStr (length
);
2264 fixed (char* dest
= tmp
) {
2266 for (int i
= 0; i
< values
.Length
; i
++) {
2267 String source
= values
[i
];
2268 if (source
!= null) {
2269 fixed (char* src
= source
) {
2270 CharCopy (dest
+ pos
, src
, source
.length
);
2272 pos
+= source
.Length
;
2279 public unsafe String
Insert (int startIndex
, String
value)
2282 throw new ArgumentNullException ("value");
2284 if (startIndex
< 0 || startIndex
> this.length
)
2285 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
2287 if (value.Length
== 0)
2289 if (this.Length
== 0)
2291 String tmp
= InternalAllocateStr (this.length
+ value.length
);
2293 fixed (char *dest
= tmp
, src
= this, val
= value) {
2295 CharCopy (dst
, src
, startIndex
);
2297 CharCopy (dst
, val
, value.length
);
2298 dst
+= value.length
;
2299 CharCopy (dst
, src
+ startIndex
, length
- startIndex
);
2304 public static string Intern (string str
)
2307 throw new ArgumentNullException ("str");
2309 return InternalIntern (str
);
2312 public static string IsInterned (string str
)
2315 throw new ArgumentNullException ("str");
2317 return InternalIsInterned (str
);
2320 #if NET_4_0 || MOONLIGHT || MOBILE
2321 public static string Join (string separator
, params string [] value)
2323 public static string Join (string separator
, string [] value)
2327 throw new ArgumentNullException ("value");
2328 if (separator
== null)
2331 return JoinUnchecked (separator
, value, 0, value.Length
);
2334 public static string Join (string separator
, string[] value, int startIndex
, int count
)
2337 throw new ArgumentNullException ("value");
2339 throw new ArgumentOutOfRangeException ("startIndex", "< 0");
2341 throw new ArgumentOutOfRangeException ("count", "< 0");
2342 if (startIndex
> value.Length
- count
)
2343 throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
2345 if (startIndex
== value.Length
)
2347 if (separator
== null)
2350 return JoinUnchecked (separator
, value, startIndex
, count
);
2353 private static unsafe string JoinUnchecked (string separator
, string[] value, int startIndex
, int count
)
2355 // Unchecked parameters
2356 // startIndex, count must be >= 0; startIndex + count must be <= value.length
2357 // separator and value must not be null
2360 int maxIndex
= startIndex
+ count
;
2361 // Precount the number of characters that the resulting string will have
2362 for (int i
= startIndex
; i
< maxIndex
; i
++) {
2363 String s
= value[i
];
2367 length
+= separator
.length
* (count
- 1);
2371 String tmp
= InternalAllocateStr (length
);
2374 fixed (char* dest
= tmp
, sepsrc
= separator
) {
2375 // Copy each string from value except the last one and add a separator for each
2377 for (int i
= startIndex
; i
< maxIndex
; i
++) {
2378 String source
= value[i
];
2379 if (source
!= null) {
2380 if (source
.Length
> 0) {
2381 fixed (char* src
= source
)
2382 CharCopy (dest
+ pos
, src
, source
.Length
);
2383 pos
+= source
.Length
;
2386 if (separator
.Length
> 0) {
2387 CharCopy (dest
+ pos
, sepsrc
, separator
.Length
);
2388 pos
+= separator
.Length
;
2391 // Append last string that does not get an additional separator
2392 String sourceLast
= value[maxIndex
];
2393 if (sourceLast
!= null) {
2394 if (sourceLast
.Length
> 0) {
2395 fixed (char* src
= sourceLast
)
2396 CharCopy (dest
+ pos
, src
, sourceLast
.Length
);
2403 bool IConvertible
.ToBoolean (IFormatProvider provider
)
2405 return Convert
.ToBoolean (this, provider
);
2408 byte IConvertible
.ToByte (IFormatProvider provider
)
2410 return Convert
.ToByte (this, provider
);
2413 char IConvertible
.ToChar (IFormatProvider provider
)
2415 return Convert
.ToChar (this, provider
);
2418 DateTime IConvertible
.ToDateTime (IFormatProvider provider
)
2420 return Convert
.ToDateTime (this, provider
);
2423 decimal IConvertible
.ToDecimal (IFormatProvider provider
)
2425 return Convert
.ToDecimal (this, provider
);
2428 double IConvertible
.ToDouble (IFormatProvider provider
)
2430 return Convert
.ToDouble (this, provider
);
2433 short IConvertible
.ToInt16 (IFormatProvider provider
)
2435 return Convert
.ToInt16 (this, provider
);
2438 int IConvertible
.ToInt32 (IFormatProvider provider
)
2440 return Convert
.ToInt32 (this, provider
);
2443 long IConvertible
.ToInt64 (IFormatProvider provider
)
2445 return Convert
.ToInt64 (this, provider
);
2448 sbyte IConvertible
.ToSByte (IFormatProvider provider
)
2450 return Convert
.ToSByte (this, provider
);
2453 float IConvertible
.ToSingle (IFormatProvider provider
)
2455 return Convert
.ToSingle (this, provider
);
2458 object IConvertible
.ToType (Type targetType
, IFormatProvider provider
)
2460 if (targetType
== null)
2461 throw new ArgumentNullException ("type");
2462 return Convert
.ToType (this, targetType
, provider
, false);
2465 ushort IConvertible
.ToUInt16 (IFormatProvider provider
)
2467 return Convert
.ToUInt16 (this, provider
);
2470 uint IConvertible
.ToUInt32 (IFormatProvider provider
)
2472 return Convert
.ToUInt32 (this, provider
);
2475 ulong IConvertible
.ToUInt64 (IFormatProvider provider
)
2477 return Convert
.ToUInt64 (this, provider
);
2486 public CharEnumerator
GetEnumerator ()
2488 return new CharEnumerator (this);
2491 IEnumerator
<char> IEnumerable
<char>.GetEnumerator ()
2493 return new CharEnumerator (this);
2496 IEnumerator IEnumerable
.GetEnumerator ()
2498 return new CharEnumerator (this);
2501 private static void ParseFormatSpecifier (string str
, ref int ptr
, out int n
, out int width
,
2502 out bool left_align
, out string format
)
2504 int max
= str
.Length
;
2506 // parses format specifier of form:
2510 // N = argument number (non-negative integer)
2512 n
= ParseDecimal (str
, ref ptr
);
2514 throw new FormatException ("Input string was not in a correct format.");
2516 // M = width (non-negative integer)
2518 if (ptr
< max
&& str
[ptr
] == ',') {
2519 // White space between ',' and number or sign.
2521 while (ptr
< max
&& Char
.IsWhiteSpace (str
[ptr
]))
2525 format
= str
.Substring (start
, ptr
- start
);
2527 left_align
= (ptr
< max
&& str
[ptr
] == '-');
2531 width
= ParseDecimal (str
, ref ptr
);
2533 throw new FormatException ("Input string was not in a correct format.");
2541 // F = argument format (string)
2543 if (ptr
< max
&& str
[ptr
] == ':') {
2545 while (ptr
< max
&& str
[ptr
] != '}')
2548 format
+= str
.Substring (start
, ptr
- start
);
2553 if ((ptr
>= max
) || str
[ptr
++] != '}')
2554 throw new FormatException ("Input string was not in a correct format.");
2557 private static int ParseDecimal (string str
, ref int ptr
)
2561 int max
= str
.Length
;
2565 if (c
< '0' || '9' < c
)
2568 n
= n
* 10 + c
- '0';
2572 if (p
== ptr
|| p
== max
)
2579 internal unsafe void InternalSetChar (int idx
, char val
)
2581 if ((uint) idx
>= (uint) Length
)
2582 throw new ArgumentOutOfRangeException ("idx");
2584 fixed (char * pStr
= &start_char
)
2590 internal unsafe void InternalSetLength (int newLength
)
2592 if (newLength
> length
)
2593 throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
2595 // zero terminate, we can pass string objects directly via pinvoke
2596 // we also zero the rest of the string, since the new GC needs to be
2597 // able to handle the changing size (it will skip the 0 bytes).
2598 fixed (char * pStr
= &start_char
) {
2599 char *p
= pStr
+ newLength
;
2600 char *end
= pStr
+ length
;
2609 [ReliabilityContractAttribute (Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
2610 // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
2611 public unsafe override int GetHashCode ()
2613 fixed (char * c
= this) {
2615 char * end
= cc
+ length
- 1;
2617 for (;cc
< end
; cc
+= 2) {
2618 h
= (h
<< 5) - h
+ *cc
;
2619 h
= (h
<< 5) - h
+ cc
[1];
2623 h
= (h
<< 5) - h
+ *cc
;
2628 #if MOONLIGHT || MOBILE || NET_4_0
2630 public static string Concat (IEnumerable
<string> values
)
2633 throw new ArgumentNullException ("values");
2635 var stringList
= new List
<string> ();
2637 foreach (var v
in values
){
2643 return ConcatInternal (stringList
.ToArray (), len
);
2646 [ComVisibleAttribute(false)]
2647 public static string Concat
<T
> (IEnumerable
<T
> values
)
2650 throw new ArgumentNullException ("values");
2652 var stringList
= new List
<string> ();
2654 foreach (var v
in values
){
2655 string sr
= v
.ToString ();
2657 stringList
.Add (sr
);
2659 return ConcatInternal (stringList
.ToArray (), len
);
2662 [ComVisibleAttribute(false)]
2663 public static string Join (string separator
, IEnumerable
<string> values
)
2665 if (separator
== null)
2666 return Concat (values
);
2669 throw new ArgumentNullException ("values");
2671 var stringList
= new List
<string> ();
2672 foreach (var v
in values
)
2675 return JoinUnchecked (separator
, stringList
.ToArray (), 0, stringList
.Count
);
2678 [ComVisibleAttribute(false)]
2679 public static string Join (string separator
, params object [] values
)
2681 if (separator
== null)
2682 return Concat (values
);
2685 throw new ArgumentNullException ("values");
2687 var strCopy
= new string [values
.Length
];
2689 foreach (var v
in values
)
2690 strCopy
[i
++] = v
.ToString ();
2692 return JoinUnchecked (separator
, strCopy
, 0, strCopy
.Length
);
2695 [ComVisible (false)]
2696 public static string Join
<T
> (string separator
, IEnumerable
<T
> values
)
2698 if (separator
== null)
2699 return Concat
<T
> (values
);
2702 throw new ArgumentNullException ("values");
2704 var stringList
= new List
<string> ();
2705 foreach (var v
in values
)
2706 stringList
.Add (v
.ToString ());
2708 return JoinUnchecked (separator
, stringList
.ToArray (), 0, stringList
.Count
);
2711 public static bool IsNullOrWhiteSpace (string value)
2713 internal static bool IsNullOrWhiteSpace (string value)
2716 if ((value == null) || (value.Length
== 0))
2718 foreach (char c
in value)
2719 if (!Char
.IsWhiteSpace (c
))
2724 internal unsafe int GetCaseInsensitiveHashCode ()
2726 fixed (char * c
= this) {
2728 char * end
= cc
+ length
- 1;
2730 for (;cc
< end
; cc
+= 2) {
2731 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (*cc
);
2732 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (cc
[1]);
2736 h
= (h
<< 5) - h
+ Char
.ToUpperInvariant (*cc
);
2741 // Certain constructors are redirected to CreateString methods with
2742 // matching argument list. The this pointer should not be used.
2743 #pragma warning disable 169
2744 private unsafe String
CreateString (sbyte* value)
2749 byte* bytes
= (byte*) value;
2753 while (bytes
++ [0] != 0)
2755 } catch (NullReferenceException
) {
2756 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2757 } catch (AccessViolationException
) {
2758 throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
2761 return CreateString (value, 0, length
, null);
2764 private unsafe String
CreateString (sbyte* value, int startIndex
, int length
)
2766 return CreateString (value, startIndex
, length
, null);
2769 private unsafe String
CreateString (sbyte* value, int startIndex
, int length
, Encoding enc
)
2772 throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
2774 throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
2775 if (value + startIndex
< value)
2776 throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
2778 bool isDefaultEncoding
;
2780 if (isDefaultEncoding
= (enc
== null)) {
2782 throw new ArgumentNullException ("value");
2786 enc
= Encoding
.Default
;
2789 byte [] bytes
= new byte [length
];
2792 fixed (byte* bytePtr
= bytes
)
2794 memcpy (bytePtr
, (byte*) (value + startIndex
), length
);
2795 } catch (NullReferenceException
) {
2796 throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
2797 } catch (AccessViolationException
) {
2798 if (!isDefaultEncoding
)
2801 throw new ArgumentOutOfRangeException ("value", "Value, startIndex and length do not refer to a valid string.");
2804 // GetString () is called even when length == 0
2805 return enc
.GetString (bytes
);
2808 unsafe string CreateString (char *value)
2818 string result
= InternalAllocateStr (i
);
2821 fixed (char *dest
= result
) {
2822 CharCopy (dest
, value, i
);
2828 unsafe string CreateString (char *value, int startIndex
, int length
)
2833 throw new ArgumentNullException ("value");
2835 throw new ArgumentOutOfRangeException ("startIndex");
2837 throw new ArgumentOutOfRangeException ("length");
2839 string result
= InternalAllocateStr (length
);
2841 fixed (char *dest
= result
) {
2842 CharCopy (dest
, value + startIndex
, length
);
2847 unsafe string CreateString (char [] val
, int startIndex
, int length
)
2850 throw new ArgumentNullException ("value");
2852 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
2854 throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
2855 if (startIndex
> val
.Length
- length
)
2856 throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
2860 string result
= InternalAllocateStr (length
);
2862 fixed (char *dest
= result
, src
= val
) {
2863 CharCopy (dest
, src
+ startIndex
, length
);
2868 unsafe string CreateString (char [] val
)
2870 if (val
== null || val
.Length
== 0)
2872 string result
= InternalAllocateStr (val
.Length
);
2874 fixed (char *dest
= result
, src
= val
) {
2875 CharCopy (dest
, src
, val
.Length
);
2880 unsafe string CreateString (char c
, int count
)
2883 throw new ArgumentOutOfRangeException ("count");
2886 string result
= InternalAllocateStr (count
);
2887 fixed (char *dest
= result
) {
2889 char *end
= p
+ count
;
2897 #pragma warning restore 169
2899 /* helpers used by the runtime as well as above or eslewhere in corlib */
2900 internal static unsafe void memset (byte *dest
, int val
, int len
)
2911 val
= val
| (val
<< 8);
2912 val
= val
| (val
<< 16);
2915 int rest
= (int)dest
& 3;
2923 } while (rest
!= 0);
2926 ((int*)dest
) [0] = val
;
2927 ((int*)dest
) [1] = val
;
2928 ((int*)dest
) [2] = val
;
2929 ((int*)dest
) [3] = val
;
2934 ((int*)dest
) [0] = val
;
2946 static unsafe void memcpy4 (byte *dest
, byte *src
, int size
) {
2947 /*while (size >= 32) {
2948 // using long is better than int and slower than double
2949 // FIXME: enable this only on correct alignment or on platforms
2950 // that can tolerate unaligned reads/writes of doubles
2951 ((double*)dest) [0] = ((double*)src) [0];
2952 ((double*)dest) [1] = ((double*)src) [1];
2953 ((double*)dest) [2] = ((double*)src) [2];
2954 ((double*)dest) [3] = ((double*)src) [3];
2959 while (size
>= 16) {
2960 ((int*)dest
) [0] = ((int*)src
) [0];
2961 ((int*)dest
) [1] = ((int*)src
) [1];
2962 ((int*)dest
) [2] = ((int*)src
) [2];
2963 ((int*)dest
) [3] = ((int*)src
) [3];
2969 ((int*)dest
) [0] = ((int*)src
) [0];
2975 ((byte*)dest
) [0] = ((byte*)src
) [0];
2981 static unsafe void memcpy2 (byte *dest
, byte *src
, int size
) {
2983 ((short*)dest
) [0] = ((short*)src
) [0];
2984 ((short*)dest
) [1] = ((short*)src
) [1];
2985 ((short*)dest
) [2] = ((short*)src
) [2];
2986 ((short*)dest
) [3] = ((short*)src
) [3];
2992 ((short*)dest
) [0] = ((short*)src
) [0];
2998 ((byte*)dest
) [0] = ((byte*)src
) [0];
3000 static unsafe void memcpy1 (byte *dest
, byte *src
, int size
) {
3002 ((byte*)dest
) [0] = ((byte*)src
) [0];
3003 ((byte*)dest
) [1] = ((byte*)src
) [1];
3004 ((byte*)dest
) [2] = ((byte*)src
) [2];
3005 ((byte*)dest
) [3] = ((byte*)src
) [3];
3006 ((byte*)dest
) [4] = ((byte*)src
) [4];
3007 ((byte*)dest
) [5] = ((byte*)src
) [5];
3008 ((byte*)dest
) [6] = ((byte*)src
) [6];
3009 ((byte*)dest
) [7] = ((byte*)src
) [7];
3015 ((byte*)dest
) [0] = ((byte*)src
) [0];
3016 ((byte*)dest
) [1] = ((byte*)src
) [1];
3022 ((byte*)dest
) [0] = ((byte*)src
) [0];
3025 internal static unsafe void memcpy (byte *dest
, byte *src
, int size
) {
3026 // FIXME: if pointers are not aligned, try to align them
3027 // so a faster routine can be used. Handle the case where
3028 // the pointers can't be reduced to have the same alignment
3029 // (just ignore the issue on x86?)
3030 if ((((int)dest
| (int)src
) & 3) != 0) {
3031 if (((int)dest
& 1) != 0 && ((int)src
& 1) != 0 && size
>= 1) {
3037 if (((int)dest
& 2) != 0 && ((int)src
& 2) != 0 && size
>= 2) {
3038 ((short*)dest
) [0] = ((short*)src
) [0];
3043 if ((((int)dest
| (int)src
) & 1) != 0) {
3044 memcpy1 (dest
, src
, size
);
3047 if ((((int)dest
| (int)src
) & 2) != 0) {
3048 memcpy2 (dest
, src
, size
);
3052 memcpy4 (dest
, src
, size
);
3055 internal static unsafe void CharCopy (char *dest
, char *src
, int count
) {
3056 // Same rules as for memcpy, but with the premise that
3057 // chars can only be aligned to even addresses if their
3058 // enclosing types are correctly aligned
3059 if ((((int)(byte*)dest
| (int)(byte*)src
) & 3) != 0) {
3060 if (((int)(byte*)dest
& 2) != 0 && ((int)(byte*)src
& 2) != 0 && count
> 0) {
3061 ((short*)dest
) [0] = ((short*)src
) [0];
3066 if ((((int)(byte*)dest
| (int)(byte*)src
) & 2) != 0) {
3067 memcpy2 ((byte*)dest
, (byte*)src
, count
* 2);
3071 memcpy4 ((byte*)dest
, (byte*)src
, count
* 2);
3074 internal static unsafe void CharCopyReverse (char *dest
, char *src
, int count
)
3078 for (int i
= count
; i
> 0; i
--) {
3085 internal static unsafe void CharCopy (String target
, int targetIndex
, String source
, int sourceIndex
, int count
)
3087 fixed (char* dest
= target
, src
= source
)
3088 CharCopy (dest
+ targetIndex
, src
+ sourceIndex
, count
);
3091 internal static unsafe void CharCopy (String target
, int targetIndex
, Char
[] source
, int sourceIndex
, int count
)
3093 fixed (char* dest
= target
, src
= source
)
3094 CharCopy (dest
+ targetIndex
, src
+ sourceIndex
, count
);
3097 // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
3098 internal static unsafe void CharCopyReverse (String target
, int targetIndex
, String source
, int sourceIndex
, int count
)
3100 fixed (char* dest
= target
, src
= source
)
3101 CharCopyReverse (dest
+ targetIndex
, src
+ sourceIndex
, count
);
3104 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3105 unsafe public extern String (char *value);
3107 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3108 unsafe public extern String (char *value, int startIndex
, int length
);
3110 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3111 unsafe public extern String (sbyte *value);
3113 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3114 unsafe public extern String (sbyte *value, int startIndex
, int length
);
3116 [CLSCompliant (false), MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3117 unsafe public extern String (sbyte *value, int startIndex
, int length
, Encoding enc
);
3119 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3120 public extern String (char [] value, int startIndex
, int length
);
3122 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3123 public extern String (char [] value);
3125 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3126 public extern String (char c
, int count
);
3128 //[MethodImplAttribute (MethodImplOptions.InternalCall)]
3129 //private extern String[] InternalSplit (char[] separator, int count, int options);
3131 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3132 internal extern static String
InternalAllocateStr (int length
);
3134 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3135 private extern static string InternalIntern (string str
);
3137 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3138 private extern static string InternalIsInterned (string str
);
3140 [MethodImplAttribute (MethodImplOptions
.InternalCall
)]
3141 private extern static int GetLOSLimit ();