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;
18 using System.Diagnostics.CodeAnalysis;
21 * Various string handling utilities
23 internal static class StringUtil {
24 internal static string CheckAndTrimString(string paramValue, string paramName) {
25 return CheckAndTrimString(paramValue, paramName, true);
28 internal static string CheckAndTrimString(string paramValue, string paramName, bool throwIfNull) {
29 return CheckAndTrimString(paramValue, paramName, throwIfNull, -1);
32 internal static string CheckAndTrimString(string paramValue, string paramName,
33 bool throwIfNull, int lengthToCheck) {
34 if (paramValue == null) {
36 throw new ArgumentNullException(paramName);
40 string trimmedValue = paramValue.Trim();
41 if (trimmedValue.Length == 0) {
42 throw new ArgumentException(
43 SR.GetString(SR.PersonalizationProviderHelper_TrimmedEmptyString,
46 if (lengthToCheck > -1 && trimmedValue.Length > lengthToCheck) {
47 throw new ArgumentException(
48 SR.GetString(SR.StringUtil_Trimmed_String_Exceed_Maximum_Length,
49 paramValue, paramName, lengthToCheck.ToString(CultureInfo.InvariantCulture)));
54 internal static bool Equals(string s1, string s2) {
59 if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
66 unsafe internal static bool Equals(string s1, int offset1, string s2, int offset2, int length) {
68 throw new ArgumentOutOfRangeException("offset1");
70 throw new ArgumentOutOfRangeException("offset2");
72 throw new ArgumentOutOfRangeException("length");
73 if ((s1 == null ? 0 : s1.Length) - offset1 < length)
74 throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset1", "length"));
75 if ((s2 == null ? 0 : s2.Length) - offset2 < length)
76 throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset2", "length"));
77 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2))
80 fixed (char * pch1 = s1, pch2 = s2) {
81 char * p1 = pch1 + offset1, p2 = pch2 + offset2;
91 internal static bool EqualsIgnoreCase(string s1, string s2) {
92 if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
95 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
98 if(s2.Length != s1.Length) {
101 return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
104 internal static bool EqualsIgnoreCase(string s1, int index1, string s2, int index2, int length) {
105 return String.Compare(s1, index1, s2, index2, length, StringComparison.OrdinalIgnoreCase) == 0;
109 internal static string StringFromWCharPtr(IntPtr ip, int length) {
111 return new string((char *) ip, 0, length);
115 internal static string StringFromCharPtr(IntPtr ip, int length) {
116 return Marshal.PtrToStringAnsi(ip, length);
120 * Determines if the string ends with the specified character.
121 * Fast, non-culture aware.
123 internal static bool StringEndsWith(string s, char c) {
125 return len != 0 && s[len - 1] == c;
129 * Determines if the first string ends with the second string.
130 * Fast, non-culture aware.
132 unsafe internal static bool StringEndsWith(string s1, string s2) {
133 int offset = s1.Length - s2.Length;
138 fixed (char * pch1=s1, pch2=s2) {
139 char * p1 = pch1 + offset, p2=pch2;
151 * Determines if the first string ends with the second string, ignoring case.
152 * Fast, non-culture aware.
154 internal static bool StringEndsWithIgnoreCase(string s1, string s2) {
155 int offset = s1.Length - s2.Length;
160 return 0 == string.Compare(s1, offset, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
164 * Determines if the string starts with the specified character.
165 * Fast, non-culture aware.
167 internal static bool StringStartsWith(string s, char c) {
168 return s.Length != 0 && (s[0] == c);
172 * Determines if the first string starts with the second string.
173 * Fast, non-culture aware.
175 unsafe internal static bool StringStartsWith(string s1, string s2) {
176 if (s2.Length > s1.Length) {
180 fixed (char * pch1=s1, pch2=s2) {
181 char * p1 = pch1, p2=pch2;
193 * Determines if the first string starts with the second string, ignoring case.
194 * Fast, non-culture aware.
196 internal static bool StringStartsWithIgnoreCase(string s1, string s2) {
197 if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
201 if (s2.Length > s1.Length) {
205 return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
208 internal unsafe static void UnsafeStringCopy(string src, int srcIndex, char[] dest, int destIndex, int len) {
209 // We do not verify the parameters in this function, as the callers are assumed
210 // to have done so. This is important for users such as HttpWriter that already
211 // do the argument checking, and are called several times per request.
212 Debug.Assert(len >= 0, "len >= 0");
214 Debug.Assert(src != null, "src != null");
215 Debug.Assert(srcIndex >= 0, "srcIndex >= 0");
216 Debug.Assert(srcIndex + len <= src.Length, "src");
218 Debug.Assert(dest != null, "dest != null");
219 Debug.Assert(destIndex >= 0, "destIndex >= 0");
220 Debug.Assert(destIndex + len <= dest.Length, "dest");
222 int cb = len * sizeof(char);
223 fixed (char * pchSrc = src, pchDest = dest) {
224 byte* pbSrc = (byte *) (pchSrc + srcIndex);
225 byte* pbDest = (byte*) (pchDest + destIndex);
227 memcpyimpl(pbSrc, pbDest, cb);
231 internal static bool StringArrayEquals(string[] a, string [] b) {
232 if ((a == null) != (b == null)) {
245 for (int i = 0; i < n; i++) {
254 // This is copied from String.GetHashCode. We want our own copy, because the result of
255 // String.GetHashCode is not guaranteed to be the same from build to build. But we want a
256 // stable hash, since we persist things to disk (e.g. precomp scenario). VSWhidbey 399279.
257 internal static int GetStringHashCode(string s) {
259 fixed (char* src = s) {
260 int hash1 = (5381 << 16) + 5381;
264 int* pint = (int*)src;
267 hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
271 hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
275 return hash1 + (hash2 * 1566083941);
280 internal static int GetNonRandomizedHashCode(string s, bool ignoreCase = false) {
281 // Preserve the default behavior when string hash randomization is off
282 if (!AppSettings.UseRandomizedStringHashAlgorithm) {
283 return ignoreCase ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(s) : s.GetHashCode();
287 s = s.ToLower(CultureInfo.InvariantCulture);
290 // Use our stable hash algorithm implementation
291 return GetStringHashCode(s);
294 // Need StringComparer implementation. It's very expensive to port the actual BCL code here
295 // Instead use the default AppDomain, because it doesn't have string hash randomization enabled.
296 // Marshal the call to reuse the default StringComparer behavior.
297 // PERF isn't optimal, so apply consideration!
298 [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control the callers.")]
299 internal static int GetNonRandomizedStringComparerHashCode(string s) {
300 // Preserve the default behavior when string hash randomization is off
301 if (!AppSettings.UseRandomizedStringHashAlgorithm) {
302 return StringComparer.InvariantCultureIgnoreCase.GetHashCode(s);
305 ApplicationManager appManager = HostingEnvironment.GetApplicationManager();
306 if (appManager != null) {
307 // Cross AppDomain call may cause marshaling of IPrincipal to fail. So strip out the exectuion context
309 ExecutionContextUtil.RunInNullExecutionContext(delegate {
310 hashCode = appManager.GetNonRandomizedStringComparerHashCode(s, ignoreCase:true);
316 // Fall back to non-compat result
317 return GetStringHashCode(s.ToLower(CultureInfo.InvariantCulture));
320 internal static int GetNullTerminatedByteArray(Encoding enc, string s, out byte[] bytes)
326 // Encoding.GetMaxByteCount is faster than GetByteCount, but will probably allocate more
327 // memory than needed. Working with small short-lived strings here, so that's probably ok.
328 bytes = new byte[enc.GetMaxByteCount(s.Length) + 1];
329 return enc.GetBytes(s, 0, s.Length, bytes, 0);
332 internal unsafe static void memcpyimpl(byte* src, byte* dest, int len) {
334 Debug.Assert(len >= 0, "Negative length in memcpyimpl!");
338 // Portable naive implementation
343 long dstAlign = (7 & (8 - (((long)dest) & 7))); // number of bytes to copy before dest is 8-byte aligned
345 while ((dstAlign > 0) && (len > 0))
356 long srcAlign = 8 - (((long)src) & 7);
363 ((int*)dest)[0] = ((int*)src)[0];
369 srcAlign = 2; // fall through to 2-byte copies
372 if ((2 == srcAlign) || (6 == srcAlign))
376 ((short*)dest)[0] = ((short*)src)[0];
394 ((long*)dest)[0] = ((long*)src)[0];
395 ((long*)dest)[1] = ((long*)src)[1];
398 } while ((len -= 16) >= 16);
401 if(len > 0) // protection against negative len and optimization for len==16*N
405 ((long*)dest)[0] = ((long*)src)[0];
411 ((int*)dest)[0] = ((int*)src)[0];
417 ((short*)dest)[0] = ((short*)src)[0];
438 ((long*)dest)[0] = ((long*)src)[0];
439 ((long*)dest)[1] = ((long*)src)[1];
441 ((int*)dest)[0] = ((int*)src)[0];
442 ((int*)dest)[1] = ((int*)src)[1];
443 ((int*)dest)[2] = ((int*)src)[2];
444 ((int*)dest)[3] = ((int*)src)[3];
448 } while ((len -= 16) >= 16);
450 if(len > 0) // protection against negative len and optimization for len==16*N
452 if ((len & 8) != 0) {
454 ((long*)dest)[0] = ((long*)src)[0];
456 ((int*)dest)[0] = ((int*)src)[0];
457 ((int*)dest)[1] = ((int*)src)[1];
462 if ((len & 4) != 0) {
463 ((int*)dest)[0] = ((int*)src)[0];
467 if ((len & 2) != 0) {
468 ((short*)dest)[0] = ((short*)src)[0];
476 #endif // FEATURE_PAL
478 internal static string[] ObjectArrayToStringArray(object[] objectArray) {
479 String[] stringKeys = new String[objectArray.Length];
480 objectArray.CopyTo(stringKeys, 0);