| .NET class String |
| 1 | // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== /*============================================================ ** ** Class: String ** ** ** Purpose: Contains headers for the String class. Actual implementations ** are in String.cpp ** ** ===========================================================*/ namespace System { using System.Text; using System; using System.Runtime.ConstrainedExecution; using System.Globalization; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using Microsoft.Win32; using System.Runtime.InteropServices; using va_list = System.ArgIterator; // // For Information on these methods, please see COMString.cpp // |
| 2 | // The String class represents a static string of characters. Many of // the String methods perform some type of transformation on the current // instance and return the result as a new String. All comparison methods are // implemented as a part of String. As with arrays, character positions // (indices) are zero-based. // // When passing a null string into a constructor in VJ and VC, the null should be // explicitly type cast to a String. // For Example: // String s = new String((String)null); // Text.Out.WriteLine(s); // |
| 3 | [System.Runtime.InteropServices.ComVisible(true)] [Serializable] public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable #if GENERICS_WORK , IComparable<String>, IEnumerable<char>, IEquatable<String> #endif { // //NOTE NOTE NOTE NOTE //These fields map directly onto the fields in an EE StringObject. See object.h for the layout. // [NonSerialized]private int m_arrayLength; [NonSerialized]private int m_stringLength; [NonSerialized]private char m_firstChar; //private static readonly char FmtMsgMarkerChar='%'; //private static readonly char FmtMsgFmtCodeChar='!'; //These are defined in Com99/src/vm/COMStringCommon.h and must be kept in [....]. private const int TrimHead = 0; private const int TrimTail = 1; private const int TrimBoth = 2; // The Empty constant holds the empty string value. //We need to call the String constructor so that the compiler doesn't mark this as a literal. //Marking this as a literal would mean that it doesn't show up as a field which we can access //from native. public static readonly String Empty = ""; // //Native Static Methods // |
| 4 | // Joins an array of strings together as one string with a separator between each original string. // public static String Join (String separator, String[] value) { if (value==null) { throw new ArgumentNullException("value"); } return Join(separator, value, 0, value.Length); } #if WIN64 private const int charPtrAlignConst = 3; private const int alignConst = 7; #else private const int charPtrAlignConst = 1; private const int alignConst = 3; #endif internal char FirstChar { get { return m_firstChar; } } // Joins an array of strings together as one string with a separator between each original string. // public unsafe static String Join(String separator, String[] value, int startIndex, int count) { //Treat null as empty string. if (separator == null) { separator = String.Empty; } //Range check the array |
| 5 | if (value == null) { throw new ArgumentNullException("value"); } if (startIndex < 0) { throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount")); } if (startIndex > value.Length - count) { throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); } //If count is 0, that skews a whole bunch of the calculations below, so just special case that. |
| 6 | if (count == 0) { return String.Empty; } int jointLength = 0; //Figure out the total length of the strings in value int endIndex = startIndex + count - 1; for (int stringToJoinIndex = startIndex; stringToJoinIndex <= endIndex; stringToJoinIndex++) { if (value[stringToJoinIndex] != null) { jointLength += value[stringToJoinIndex].Length; } } //Add enough room for the separator. jointLength += (count - 1) * separator.Length; // Note that we may not catch all overflows with this check (since we could have wrapped around the 4gb range any number of times // and landed back in the positive range.) The input array might be modifed from other threads, // so we have to do an overflow check before each append below anyway. Those overflows will get caught down there. if ((jointLength < 0) || ((jointLength + 1) < 0) ) { throw new OutOfMemoryException(); } |
| 7 | //If this is an empty string, just return. if (jointLength == 0) { return String.Empty; } string jointString = FastAllocateString( jointLength ); fixed (char * pointerToJointString = &jointString.m_firstChar) { UnSafeCharBuffer charBuffer = new UnSafeCharBuffer( pointerToJointString, jointLength); // Append the first string first and then append each following string prefixed by the separator. charBuffer.AppendString( value[startIndex] ); for (int stringToJoinIndex = startIndex + 1; stringToJoinIndex <= endIndex; stringToJoinIndex++) { charBuffer.AppendString( separator ); charBuffer.AppendString( value[stringToJoinIndex] ); } BCLDebug.Assert(*(pointerToJointString + charBuffer.Length) == '\0', "String must be null-terminated!"); } return jointString; } [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern int nativeCompareOrdinal(String strA, String strB, bool bIgnoreCase); [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern int nativeCompareOrdinalEx(String strA, int indexA, String strB, int indexB, int count); //This will not work in case-insensitive mode for any character greater than 0x80. //We'll throw an ArgumentException. [MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe internal static extern int nativeCompareOrdinalWC(String strA, char *strBChars, bool bIgnoreCase, out bool success); // // This is a helper method for the security team. They need to uppercase some strings (guaranteed to be less // than 0x80) before security is fully initialized. Without security initialized, we can't grab resources (the nlp's) // from the assembly. This provides a workaround for that problem and should NOT be used anywhere else. // internal unsafe static string SmallCharToUpper(string strIn) { BCLDebug.Assert(strIn != null, "strIn"); // // Get the length and pointers to each of the buffers. Walk the length // of the string and copy the characters from the inBuffer to the outBuffer, // capitalizing it if necessary. We assert that all of our characters are // less than 0x80. // int length = strIn.Length; String strOut = FastAllocateString(length); fixed (char * inBuff = &strIn.m_firstChar, outBuff = &strOut.m_firstChar) { char c; int upMask = ~0x20; for(int i = 0; i < length; i++) { c = inBuff[i]; BCLDebug.Assert((int)c < 0x80, "(int)c < 0x80"); |
| 8 | // // 0x20 is the difference between upper and lower characters in the lower // 128 ASCII characters. And this bit off to make the chars uppercase. // if (c >= 'a' && c <= 'z') { c = (char)((int)c & upMask); } outBuff[i] = c; } BCLDebug.Assert(outBuff[length]=='\0', "outBuff[length]=='\0'"); } return strOut; } // // // NATIVE INSTANCE METHODS // // // // Search/Query methods // [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] |
| 9 | private unsafe static bool EqualsHelper(String strA, String strB) { int length = strA.Length; if (length != strB.Length) return false; fixed(char* ap = strA) fixed(char* bp = strB) { char* a = ap; char* b = bp; // unroll the loop #if AMD64 // for AMD64 bit platform we unroll by 12 and // check 3 qword at a time. This is less code // than the 32 bit case and is shorter // pathlength while (length >= 12) { if (*(long*)a != *(long*)b) break; if (*(long*)(a+4) != *(long*)(b+4)) break; if (*(long*)(a+8) != *(long*)(b+8)) break; a += 12; b += 12; length -= 12; } #else while (length >= 10) { if (*(int*)a != *(int*)b) break; if (*(int*)(a+2) != *(int*)(b+2)) break; if (*(int*)(a+4) != *(int*)(b+4)) break; if (*(int*)(a+6) != *(int*)(b+6)) break; if (*(int*)(a+8) != *(int*)(b+8)) break; a += 10; b += 10; length -= 10; } #endif |
| 10 | // This depends on the fact that the String objects are // always zero terminated and that the terminating zero is not included // in the length. For odd string sizes, the last compare will include // the zero terminator. while (length > 0) { if (*(int*)a != *(int*)b) break; a += 2; b += 2; length -= 2; } return (length <= 0); |
| 11 | } } private unsafe static int CompareOrdinalHelper(String strA, String strB) { BCLDebug.Assert(strA != null && strB != null, "strings cannot be null!"); int length = Math.Min(strA.Length, strB.Length); int diffOffset = -1; fixed(char* ap = strA) fixed(char* bp = strB) { char* a = ap; char* b = bp; // unroll the loop while (length >= 10) { if (*(int*)a != *(int*)b) { diffOffset = 0; break; } if (*(int*)(a+2) != *(int*)(b+2)) { diffOffset = 2; break; } if (*(int*)(a+4) != *(int*)(b+4)) { diffOffset = 4; break; } if (*(int*)(a+6) != *(int*)(b+6)) { |
| 12 | diffOffset = 6; break; } if (*(int*)(a+8) != *(int*)(b+8)) { diffOffset = 8; break; } a += 10; b += 10; length -= 10; } if( diffOffset != -1) { // we already see a difference in the unrolled loop above a += diffOffset; b += diffOffset; int order; if ( (order = (int)*a - (int)*b) != 0) { return order; } BCLDebug.Assert( *(a+1) != *(b+1), "This byte must be different if we reach here!"); return ((int)*(a+1) - (int)*(b+1)); } // now go back to slower code path and do comparison on 4 bytes one time. // Following code also take advantage of the fact strings will // use even numbers of characters (runtime will have a extra zero at the end.) // so even if length is 1 here, we can still do the comparsion. while (length > 0) { if (*(int*)a != *(int*)b) { |
| 13 | break; } a += 2; b += 2; length -= 2; } if( length > 0) { int c; // found a different int on above loop if ( (c = (int)*a - (int)*b) != 0) { return c; } BCLDebug.Assert( *(a+1) != *(b+1), "This byte must be different if we reach here!"); return ((int)*(a+1) - (int)*(b+1)); } // At this point, we have compared all the characters in at least one string. // The longer string will be larger. return strA.Length - strB.Length; } } // Determines whether two strings match. |
| 14 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public override bool Equals(Object obj) { String str = obj as String; if (str == null) { // exception will be thrown later for null this if (this != null) return false; } return EqualsHelper(this, str); } // Determines whether two strings match. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public bool Equals(String value) { if (value == null) { // exception will be thrown later for null this if (this != null) return false; } return EqualsHelper(this, value); } public bool Equals(String value, StringComparison comparisonType) { if( comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) { throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } |
| 15 | if( (Object)this == (Object)value) { return true; } if( (Object)value == null) { return false; } switch (comparisonType) { case StringComparison.CurrentCulture: return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0); case StringComparison.CurrentCultureIgnoreCase: return (CultureInfo.CurrentCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0); case StringComparison.InvariantCulture: return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.None) == 0); case StringComparison.InvariantCultureIgnoreCase: return (CultureInfo.InvariantCulture.CompareInfo.Compare(this, value, CompareOptions.IgnoreCase) == 0); case StringComparison.Ordinal: return this.Equals(value); case StringComparison.OrdinalIgnoreCase: if( this.Length != value.Length) return false; else { // If both strings are ASCII strings, we can take the fast path. if (this.IsAscii() && value.IsAscii()) { return (String.nativeCompareOrdinal(this, value, true) == 0); } // Take the slow path. return (TextInfo.CompareOrdinalIgnoreCase(this, value) == 0); } default: throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } } |
| 16 | // Determines whether two Strings match. public static bool Equals(String a, String b) { if ((Object)a==(Object)b) { return true; } if ((Object)a==null || (Object)b==null) { return false; } return EqualsHelper(a, b); } public static bool Equals(String a, String b, StringComparison comparisonType) { if( comparisonType < StringComparison.CurrentCulture || comparisonType > StringComparison.OrdinalIgnoreCase) { throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } if ((Object)a==(Object)b) { return true; } if ((Object)a==null || (Object)b==null) { return false; } |
| 17 | switch (comparisonType) { case StringComparison.CurrentCulture: return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0); case StringComparison.CurrentCultureIgnoreCase: return (CultureInfo.CurrentCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0); case StringComparison.InvariantCulture: return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.None) == 0); case StringComparison.InvariantCultureIgnoreCase: return (CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0); case StringComparison.Ordinal: return EqualsHelper(a, b); case StringComparison.OrdinalIgnoreCase: if( a.Length != b.Length) return false; else { // If both strings are ASCII strings, we can take the fast path. if (a.IsAscii() && b.IsAscii()) { return (String.nativeCompareOrdinal(a, b, true) == 0); } // Take the slow path. return (TextInfo.CompareOrdinalIgnoreCase(a, b) == 0); } default: throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType"); } } |
Комментарии