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