1 //------------------------------------------------------------------------------
2 // <copyright file="StringUtil.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
10 * Copyright (c) 1999 Microsoft Corporation
13 namespace System.Web.Util {
15 using System.Globalization;
16 using System.Runtime.InteropServices;
17 using System.Web.Hosting;
20 * Various string handling utilities
22 internal static class StringUtil {
23 internal static string CheckAndTrimString(string paramValue, string paramName) {
24 return CheckAndTrimString(paramValue, paramName, true);
27 internal static string CheckAndTrimString(string paramValue, string paramName, bool throwIfNull) {
28 return CheckAndTrimString(paramValue, paramName, throwIfNull, -1);
31 internal static string CheckAndTrimString(string paramValue, string paramName,
32 bool throwIfNull, int lengthToCheck) {
33 if (paramValue == null) {
35 throw new ArgumentNullException(paramName);
39 string trimmedValue = paramValue.Trim();
40 if (trimmedValue.Length == 0) {
41 throw new ArgumentException(
42 SR.GetString(SR.PersonalizationProviderHelper_TrimmedEmptyString,
45 if (lengthToCheck > -1 && trimmedValue.Length > lengthToCheck) {
46 throw new ArgumentException(
47 SR.GetString(SR.StringUtil_Trimmed_String_Exceed_Maximum_Length,
48 paramValue, paramName, lengthToCheck.ToString(CultureInfo.InvariantCulture)));
53 internal static bool Equals(string s1, string s2) {
58 if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
65 unsafe internal static bool Equals(string s1, int offset1, string s2, int offset2, int length) {
67 throw new ArgumentOutOfRangeException("offset1");
69 throw new ArgumentOutOfRangeException("offset2");
71 throw new ArgumentOutOfRangeException("length");
72 if ((s1 == null ? 0 : s1.Length) - offset1 < length)
73 throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset1", "length"));
74 if ((s2 == null ? 0 : s2.Length) - offset2 < length)
75 throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset2", "length"));
76 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2))
79 fixed (char * pch1 = s1, pch2 = s2) {
80 char * p1 = pch1 + offset1, p2 = pch2 + offset2;
90 internal static bool EqualsIgnoreCase(string s1, string s2) {
91 if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
94 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
97 if(s2.Length != s1.Length) {
100 return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
103 internal static bool EqualsIgnoreCase(string s1, int index1, string s2, int index2, int length) {
104 return String.Compare(s1, index1, s2, index2, length, StringComparison.OrdinalIgnoreCase) == 0;
108 internal static string StringFromWCharPtr(IntPtr ip, int length) {
110 return new string((char *) ip, 0, length);
114 internal static string StringFromCharPtr(IntPtr ip, int length) {
115 return Marshal.PtrToStringAnsi(ip, length);
119 * Determines if the string ends with the specified character.
120 * Fast, non-culture aware.
122 internal static bool StringEndsWith(string s, char c) {
124 return len != 0 && s[len - 1] == c;
128 * Determines if the first string ends with the second string.
129 * Fast, non-culture aware.
131 unsafe internal static bool StringEndsWith(string s1, string s2) {
132 int offset = s1.Length - s2.Length;
137 fixed (char * pch1=s1, pch2=s2) {
138 char * p1 = pch1 + offset, p2=pch2;
150 * Determines if the first string ends with the second string, ignoring case.
151 * Fast, non-culture aware.
153 internal static bool StringEndsWithIgnoreCase(string s1, string s2) {
154 int offset = s1.Length - s2.Length;
159 return 0 == string.Compare(s1, offset, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
163 * Determines if the string starts with the specified character.
164 * Fast, non-culture aware.
166 internal static bool StringStartsWith(string s, char c) {
167 return s.Length != 0 && (s[0] == c);
171 * Determines if the first string starts with the second string.
172 * Fast, non-culture aware.
174 unsafe internal static bool StringStartsWith(string s1, string s2) {
175 if (s2.Length > s1.Length) {
179 fixed (char * pch1=s1, pch2=s2) {
180 char * p1 = pch1, p2=pch2;
192 * Determines if the first string starts with the second string, ignoring case.
193 * Fast, non-culture aware.
195 internal static bool StringStartsWithIgnoreCase(string s1, string s2) {
196 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
200 if (s2.Length > s1.Length) {
204 return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
207 internal unsafe static void UnsafeStringCopy(string src, int srcIndex, char[] dest, int destIndex, int len) {
208 // We do not verify the parameters in this function, as the callers are assumed
209 // to have done so. This is important for users such as HttpWriter that already
210 // do the argument checking, and are called several times per request.
211 Debug.Assert(len >= 0, "len >= 0");
213 Debug.Assert(src != null, "src != null");
214 Debug.Assert(srcIndex >= 0, "srcIndex >= 0");
215 Debug.Assert(srcIndex + len <= src.Length, "src");
217 Debug.Assert(dest != null, "dest != null");
218 Debug.Assert(destIndex >= 0, "destIndex >= 0");
219 Debug.Assert(destIndex + len <= dest.Length, "dest");
221 int cb = len * sizeof(char);
222 fixed (char * pchSrc = src, pchDest = dest) {
223 byte* pbSrc = (byte *) (pchSrc + srcIndex);
224 byte* pbDest = (byte*) (pchDest + destIndex);
226 memcpyimpl(pbSrc, pbDest, cb);
230 internal static bool StringArrayEquals(string[] a, string [] b) {
231 if ((a == null) != (b == null)) {
244 for (int i = 0; i < n; i++) {
253 // This is copied from String.GetHashCode. We want our own copy, because the result of
254 // String.GetHashCode is not guaranteed to be the same from build to build. But we want a
255 // stable hash, since we persist things to disk (e.g. precomp scenario). VSWhidbey 399279.
256 internal static int GetStringHashCode(string s) {
258 fixed (char* src = s) {
259 int hash1 = (5381 << 16) + 5381;
263 int* pint = (int*)src;
266 hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
270 hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
274 return hash1 + (hash2 * 1566083941);
279 internal static int GetNonRandomizedHashCode(string s, bool ignoreCase = false) {
280 // Preserve the default behavior when string hash randomization is off
281 if (!AppSettings.UseRandomizedStringHashAlgorithm) {
282 return ignoreCase ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(s) : s.GetHashCode();
286 s = s.ToLower(CultureInfo.InvariantCulture);
289 // Use our stable hash algorithm implementation
290 return GetStringHashCode(s);
293 // Need StringComparer implementation. It's very expensive to port the actual BCL code here
294 // Instead use the default AppDomain, because it doesn't have string hash randomization enabled.
295 // Marshal the call to reuse the default StringComparer behavior.
296 // PERF isn't optimal, so apply consideration!
297 internal static int GetNonRandomizedStringComparerHashCode(string s) {
298 // Preserve the default behavior when string hash randomization is off
299 if (!AppSettings.UseRandomizedStringHashAlgorithm) {
300 return StringComparer.InvariantCultureIgnoreCase.GetHashCode(s);
303 ApplicationManager appManager = HostingEnvironment.GetApplicationManager();
304 if (appManager != null) {
305 // Cross AppDomain call may cause marshaling of IPrincipal to fail. So strip out the exectuion context
307 ExecutionContextUtil.RunInNullExecutionContext(delegate {
308 hashCode = appManager.GetNonRandomizedStringComparerHashCode(s, ignoreCase:true);
314 // Fall back to non-compat result
315 return GetStringHashCode(s.ToLower(CultureInfo.InvariantCulture));
318 internal static int GetNullTerminatedByteArray(Encoding enc, string s, out byte[] bytes)
324 // Encoding.GetMaxByteCount is faster than GetByteCount, but will probably allocate more
325 // memory than needed. Working with small short-lived strings here, so that's probably ok.
326 bytes = new byte[enc.GetMaxByteCount(s.Length) + 1];
327 return enc.GetBytes(s, 0, s.Length, bytes, 0);
330 internal unsafe static void memcpyimpl(byte* src, byte* dest, int len) {
332 Debug.Assert(len >= 0, "Negative length in memcpyimpl!");
336 // Portable naive implementation
341 long dstAlign = (7 & (8 - (((long)dest) & 7))); // number of bytes to copy before dest is 8-byte aligned
343 while ((dstAlign > 0) && (len > 0))
354 long srcAlign = 8 - (((long)src) & 7);
361 ((int*)dest)[0] = ((int*)src)[0];
367 srcAlign = 2; // fall through to 2-byte copies
370 if ((2 == srcAlign) || (6 == srcAlign))
374 ((short*)dest)[0] = ((short*)src)[0];
392 ((long*)dest)[0] = ((long*)src)[0];
393 ((long*)dest)[1] = ((long*)src)[1];
396 } while ((len -= 16) >= 16);
399 if(len > 0) // protection against negative len and optimization for len==16*N
403 ((long*)dest)[0] = ((long*)src)[0];
409 ((int*)dest)[0] = ((int*)src)[0];
415 ((short*)dest)[0] = ((short*)src)[0];
436 ((long*)dest)[0] = ((long*)src)[0];
437 ((long*)dest)[1] = ((long*)src)[1];
439 ((int*)dest)[0] = ((int*)src)[0];
440 ((int*)dest)[1] = ((int*)src)[1];
441 ((int*)dest)[2] = ((int*)src)[2];
442 ((int*)dest)[3] = ((int*)src)[3];
446 } while ((len -= 16) >= 16);
448 if(len > 0) // protection against negative len and optimization for len==16*N
450 if ((len & 8) != 0) {
452 ((long*)dest)[0] = ((long*)src)[0];
454 ((int*)dest)[0] = ((int*)src)[0];
455 ((int*)dest)[1] = ((int*)src)[1];
460 if ((len & 4) != 0) {
461 ((int*)dest)[0] = ((int*)src)[0];
465 if ((len & 2) != 0) {
466 ((short*)dest)[0] = ((short*)src)[0];
474 #endif // FEATURE_PAL
476 internal static string[] ObjectArrayToStringArray(object[] objectArray) {
477 String[] stringKeys = new String[objectArray.Length];
478 objectArray.CopyTo(stringKeys, 0);