Merge pull request #3213 from henricm/fix-for-win-securestring-to-bstr
[mono.git] / mcs / class / referencesource / System.Web / Util / StringUtil.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="StringUtil.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 /*
8  * StringUtil class
9  * 
10  * Copyright (c) 1999 Microsoft Corporation
11  */
12
13 namespace System.Web.Util {
14 using System.Text;
15 using System.Globalization;
16 using System.Runtime.InteropServices;
17 using System.Web.Hosting;
18 using System.Diagnostics.CodeAnalysis;
19
20 /*
21  * Various string handling utilities
22  */
23 internal static class StringUtil {
24     internal static string CheckAndTrimString(string paramValue, string paramName) {
25         return CheckAndTrimString(paramValue, paramName, true);
26     }
27
28     internal static string CheckAndTrimString(string paramValue, string paramName, bool throwIfNull) {
29         return CheckAndTrimString(paramValue, paramName, throwIfNull, -1);
30     }
31
32     internal static string CheckAndTrimString(string paramValue, string paramName,
33                                               bool throwIfNull, int lengthToCheck) {
34         if (paramValue == null) {
35             if (throwIfNull) {
36                 throw new ArgumentNullException(paramName);
37             }
38             return null;
39         }
40         string trimmedValue = paramValue.Trim();
41         if (trimmedValue.Length == 0) {
42             throw new ArgumentException(
43                 SR.GetString(SR.PersonalizationProviderHelper_TrimmedEmptyString,
44                                                  paramName));
45         }
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)));
50         }
51         return trimmedValue;
52     }
53
54     internal static bool Equals(string s1, string s2) {
55         if (s1 == s2) {
56             return true;
57         }
58
59         if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
60             return true;
61         }
62
63         return false;
64     }
65
66     unsafe internal static bool Equals(string s1, int offset1, string s2, int offset2, int length) {
67         if (offset1 < 0)
68             throw new ArgumentOutOfRangeException("offset1");
69         if (offset2 < 0)
70             throw new ArgumentOutOfRangeException("offset2");
71         if (length < 0)
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))
78             return true;
79
80         fixed (char * pch1 = s1, pch2 = s2) {
81             char * p1 = pch1 + offset1, p2 = pch2 + offset2;
82             int c = length;
83             while (c-- > 0) {
84                 if (*p1++ != *p2++)
85                     return false;
86             }
87         }
88         return true;
89     }
90
91     internal static bool EqualsIgnoreCase(string s1, string s2) {
92         if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
93             return true;
94         }
95         if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
96             return false;
97         }
98         if(s2.Length != s1.Length) {
99             return false;
100         }
101         return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
102     }
103
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;
106     }
107
108
109     internal static string StringFromWCharPtr(IntPtr ip, int length) {
110         unsafe {
111             return new string((char *) ip, 0, length);
112         }
113     }
114
115     internal static string StringFromCharPtr(IntPtr ip, int length) {
116         return Marshal.PtrToStringAnsi(ip, length);
117     }
118
119     /*
120      * Determines if the string ends with the specified character.
121      * Fast, non-culture aware.  
122      */
123     internal static bool StringEndsWith(string s, char c) {
124         int len = s.Length;
125         return len != 0 && s[len - 1] == c;
126     }
127
128     /*
129      * Determines if the first string ends with the second string.
130      * Fast, non-culture aware.  
131      */
132     unsafe internal static bool StringEndsWith(string s1, string s2) {
133         int offset = s1.Length - s2.Length;
134         if (offset < 0) {
135             return false;
136         }
137         
138         fixed (char * pch1=s1, pch2=s2) {
139             char * p1 = pch1 + offset, p2=pch2;
140             int c = s2.Length;
141             while (c-- > 0) {
142                 if (*p1++ != *p2++)
143                     return false;
144             }
145         }
146
147         return true;
148     }
149
150     /*
151      * Determines if the first string ends with the second string, ignoring case.
152      * Fast, non-culture aware.  
153      */
154     internal static bool StringEndsWithIgnoreCase(string s1, string s2) {
155         int offset = s1.Length - s2.Length;
156         if (offset < 0) {
157             return false;
158         }
159
160         return 0 == string.Compare(s1, offset, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
161     }
162
163     /*
164      * Determines if the string starts with the specified character.
165      * Fast, non-culture aware.  
166      */
167     internal static bool StringStartsWith(string s, char c) {
168         return s.Length != 0 && (s[0] == c);
169     }
170
171     /*
172      * Determines if the first string starts with the second string.
173      * Fast, non-culture aware.  
174      */
175     unsafe internal static bool StringStartsWith(string s1, string s2) {
176         if (s2.Length > s1.Length) {
177             return false;
178         }
179         
180         fixed (char * pch1=s1, pch2=s2) {
181             char * p1 = pch1, p2=pch2;
182             int c = s2.Length;
183             while (c-- > 0) {
184                 if (*p1++ != *p2++)
185                     return false;
186             }
187         }
188
189         return true;
190     }
191
192     /*
193      * Determines if the first string starts with the second string, ignoring case.
194      * Fast, non-culture aware.  
195      */
196     internal static bool StringStartsWithIgnoreCase(string s1, string s2) {
197         if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
198             return false;
199         }
200
201         if (s2.Length > s1.Length) {
202             return false;
203         }
204
205         return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
206     }
207 #if !MONO
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");
213
214         Debug.Assert(src != null, "src != null");
215         Debug.Assert(srcIndex >= 0, "srcIndex >= 0");
216         Debug.Assert(srcIndex + len <= src.Length, "src");
217
218         Debug.Assert(dest != null, "dest != null");
219         Debug.Assert(destIndex >= 0, "destIndex >= 0");
220         Debug.Assert(destIndex + len <= dest.Length, "dest");
221
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);
226
227             memcpyimpl(pbSrc, pbDest, cb);
228         }
229     }
230 #endif
231     internal static bool StringArrayEquals(string[] a, string [] b) {
232         if ((a == null) != (b == null)) {
233             return false;
234         }
235
236         if (a == null) {
237             return true;
238         }
239
240         int n = a.Length;
241         if (n != b.Length) {
242             return false;
243         }
244
245         for (int i = 0; i < n; i++) {
246             if (a[i] != b[i]) {
247                 return false;
248             }
249         }
250
251         return true;
252     }
253
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) {
258         unsafe {
259             fixed (char* src = s) {
260                 int hash1 = (5381 << 16) + 5381;
261                 int hash2 = hash1;
262
263                 // 32bit machines.
264                 int* pint = (int*)src;
265                 int len = s.Length;
266                 while (len > 0) {
267                     hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
268                     if (len <= 2) {
269                         break;
270                     }
271                     hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
272                     pint += 2;
273                     len -= 4;
274                 }
275                 return hash1 + (hash2 * 1566083941);
276             }
277         }
278     }
279 #if !MONO
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();
284         }
285
286         if (ignoreCase) {
287             s = s.ToLower(CultureInfo.InvariantCulture);
288         }
289
290         // Use our stable hash algorithm implementation
291         return GetStringHashCode(s);
292     }
293
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);
303         }
304
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
308             int hashCode = 0;
309             ExecutionContextUtil.RunInNullExecutionContext(delegate {
310                  hashCode = appManager.GetNonRandomizedStringComparerHashCode(s, ignoreCase:true);
311             });
312
313             return hashCode;
314         }
315
316         // Fall back to non-compat result
317         return GetStringHashCode(s.ToLower(CultureInfo.InvariantCulture));
318     }
319 #endif
320     internal static int GetNullTerminatedByteArray(Encoding enc, string s, out byte[] bytes)
321     {
322         bytes = null;
323         if (s == null)
324                 return 0;
325
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);
330     }
331
332     internal unsafe static void memcpyimpl(byte* src, byte* dest, int len) {
333 #if !MONO        
334         Debug.Assert(len >= 0, "Negative length in memcpyimpl!");
335 #endif
336
337 #if FEATURE_PAL
338         // Portable naive implementation
339         while (len-- > 0)
340             *dest++ = *src++;
341 #else
342 #if IA64
343         long dstAlign = (7 & (8 - (((long)dest) & 7))); // number of bytes to copy before dest is 8-byte aligned
344
345         while ((dstAlign > 0) && (len > 0))
346         {
347             // <
348             *dest++ = *src++;
349             
350             len--;
351             dstAlign--;
352         }
353
354         if (len > 0)
355         {
356             long srcAlign = 8 - (((long)src) & 7);
357             if (srcAlign != 8)
358             {
359                 if (4 == srcAlign)
360                 {
361                     while (len >= 4)
362                     {
363                         ((int*)dest)[0] = ((int*)src)[0];
364                         dest += 4;
365                         src  += 4;
366                         len  -= 4;
367                     }
368                     
369                     srcAlign = 2;   // fall through to 2-byte copies
370                 }
371                 
372                 if ((2 == srcAlign) || (6 == srcAlign))
373                 {
374                     while (len >= 2)
375                     {
376                         ((short*)dest)[0] = ((short*)src)[0];
377                         dest += 2;
378                         src  += 2;
379                         len  -= 2;
380                     }
381                 }
382                 
383                 while (len-- > 0)
384                 {
385                     *dest++ = *src++;
386                 }
387             }
388             else
389             {
390                 if (len >= 16) 
391                 {
392                     do 
393                     {
394                         ((long*)dest)[0] = ((long*)src)[0];
395                         ((long*)dest)[1] = ((long*)src)[1];
396                         dest += 16;
397                         src += 16;
398                     } while ((len -= 16) >= 16);
399                 }
400                 
401                 if(len > 0)  // protection against negative len and optimization for len==16*N
402                 {
403                     if ((len & 8) != 0) 
404                     {
405                         ((long*)dest)[0] = ((long*)src)[0];
406                         dest += 8;
407                         src += 8;
408                     }
409                     if ((len & 4) != 0) 
410                     {
411                         ((int*)dest)[0] = ((int*)src)[0];
412                         dest += 4;
413                         src += 4;
414                     }
415                     if ((len & 2) != 0) 
416                     {
417                         ((short*)dest)[0] = ((short*)src)[0];
418                         dest += 2;
419                         src += 2;
420                     }
421                     if ((len & 1) != 0)
422                     {
423                         *dest++ = *src++;
424                     }
425                 }
426             }
427         }
428 #else
429         // <
430
431
432
433
434
435         if (len >= 16) {
436             do {
437 #if AMD64
438                 ((long*)dest)[0] = ((long*)src)[0];
439                 ((long*)dest)[1] = ((long*)src)[1];
440 #else
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];
445 #endif
446                 dest += 16;
447                 src += 16;
448             } while ((len -= 16) >= 16);
449         }
450         if(len > 0)  // protection against negative len and optimization for len==16*N
451         {
452             if ((len & 8) != 0) {
453 #if AMD64
454                 ((long*)dest)[0] = ((long*)src)[0];
455 #else
456                 ((int*)dest)[0] = ((int*)src)[0];
457                 ((int*)dest)[1] = ((int*)src)[1];
458 #endif
459                 dest += 8;
460                 src += 8;
461             }
462             if ((len & 4) != 0) {
463                 ((int*)dest)[0] = ((int*)src)[0];
464                 dest += 4;
465                 src += 4;
466             }
467             if ((len & 2) != 0) {
468                 ((short*)dest)[0] = ((short*)src)[0];
469                 dest += 2;
470                 src += 2;
471             }
472             if ((len & 1) != 0)
473                 *dest++ = *src++;
474         }
475 #endif
476 #endif // FEATURE_PAL
477     }
478     internal static string[] ObjectArrayToStringArray(object[] objectArray) {
479         String[] stringKeys = new String[objectArray.Length];
480         objectArray.CopyTo(stringKeys, 0);
481         return stringKeys;
482     }
483
484 }
485 }