94b2861243
Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
486 lines
16 KiB
C#
486 lines
16 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="StringUtil.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* StringUtil class
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*/
|
|
|
|
namespace System.Web.Util {
|
|
using System.Text;
|
|
using System.Globalization;
|
|
using System.Runtime.InteropServices;
|
|
using System.Web.Hosting;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
/*
|
|
* Various string handling utilities
|
|
*/
|
|
internal static class StringUtil {
|
|
internal static string CheckAndTrimString(string paramValue, string paramName) {
|
|
return CheckAndTrimString(paramValue, paramName, true);
|
|
}
|
|
|
|
internal static string CheckAndTrimString(string paramValue, string paramName, bool throwIfNull) {
|
|
return CheckAndTrimString(paramValue, paramName, throwIfNull, -1);
|
|
}
|
|
|
|
internal static string CheckAndTrimString(string paramValue, string paramName,
|
|
bool throwIfNull, int lengthToCheck) {
|
|
if (paramValue == null) {
|
|
if (throwIfNull) {
|
|
throw new ArgumentNullException(paramName);
|
|
}
|
|
return null;
|
|
}
|
|
string trimmedValue = paramValue.Trim();
|
|
if (trimmedValue.Length == 0) {
|
|
throw new ArgumentException(
|
|
SR.GetString(SR.PersonalizationProviderHelper_TrimmedEmptyString,
|
|
paramName));
|
|
}
|
|
if (lengthToCheck > -1 && trimmedValue.Length > lengthToCheck) {
|
|
throw new ArgumentException(
|
|
SR.GetString(SR.StringUtil_Trimmed_String_Exceed_Maximum_Length,
|
|
paramValue, paramName, lengthToCheck.ToString(CultureInfo.InvariantCulture)));
|
|
}
|
|
return trimmedValue;
|
|
}
|
|
|
|
internal static bool Equals(string s1, string s2) {
|
|
if (s1 == s2) {
|
|
return true;
|
|
}
|
|
|
|
if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
unsafe internal static bool Equals(string s1, int offset1, string s2, int offset2, int length) {
|
|
if (offset1 < 0)
|
|
throw new ArgumentOutOfRangeException("offset1");
|
|
if (offset2 < 0)
|
|
throw new ArgumentOutOfRangeException("offset2");
|
|
if (length < 0)
|
|
throw new ArgumentOutOfRangeException("length");
|
|
if ((s1 == null ? 0 : s1.Length) - offset1 < length)
|
|
throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset1", "length"));
|
|
if ((s2 == null ? 0 : s2.Length) - offset2 < length)
|
|
throw new ArgumentOutOfRangeException(SR.GetString(SR.InvalidOffsetOrCount, "offset2", "length"));
|
|
if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2))
|
|
return true;
|
|
|
|
fixed (char * pch1 = s1, pch2 = s2) {
|
|
char * p1 = pch1 + offset1, p2 = pch2 + offset2;
|
|
int c = length;
|
|
while (c-- > 0) {
|
|
if (*p1++ != *p2++)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal static bool EqualsIgnoreCase(string s1, string s2) {
|
|
if (String.IsNullOrEmpty(s1) && String.IsNullOrEmpty(s2)) {
|
|
return true;
|
|
}
|
|
if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
|
|
return false;
|
|
}
|
|
if(s2.Length != s1.Length) {
|
|
return false;
|
|
}
|
|
return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
internal static bool EqualsIgnoreCase(string s1, int index1, string s2, int index2, int length) {
|
|
return String.Compare(s1, index1, s2, index2, length, StringComparison.OrdinalIgnoreCase) == 0;
|
|
}
|
|
|
|
|
|
internal static string StringFromWCharPtr(IntPtr ip, int length) {
|
|
unsafe {
|
|
return new string((char *) ip, 0, length);
|
|
}
|
|
}
|
|
|
|
internal static string StringFromCharPtr(IntPtr ip, int length) {
|
|
return Marshal.PtrToStringAnsi(ip, length);
|
|
}
|
|
|
|
/*
|
|
* Determines if the string ends with the specified character.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
internal static bool StringEndsWith(string s, char c) {
|
|
int len = s.Length;
|
|
return len != 0 && s[len - 1] == c;
|
|
}
|
|
|
|
/*
|
|
* Determines if the first string ends with the second string.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
unsafe internal static bool StringEndsWith(string s1, string s2) {
|
|
int offset = s1.Length - s2.Length;
|
|
if (offset < 0) {
|
|
return false;
|
|
}
|
|
|
|
fixed (char * pch1=s1, pch2=s2) {
|
|
char * p1 = pch1 + offset, p2=pch2;
|
|
int c = s2.Length;
|
|
while (c-- > 0) {
|
|
if (*p1++ != *p2++)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Determines if the first string ends with the second string, ignoring case.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
internal static bool StringEndsWithIgnoreCase(string s1, string s2) {
|
|
int offset = s1.Length - s2.Length;
|
|
if (offset < 0) {
|
|
return false;
|
|
}
|
|
|
|
return 0 == string.Compare(s1, offset, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
/*
|
|
* Determines if the string starts with the specified character.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
internal static bool StringStartsWith(string s, char c) {
|
|
return s.Length != 0 && (s[0] == c);
|
|
}
|
|
|
|
/*
|
|
* Determines if the first string starts with the second string.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
unsafe internal static bool StringStartsWith(string s1, string s2) {
|
|
if (s2.Length > s1.Length) {
|
|
return false;
|
|
}
|
|
|
|
fixed (char * pch1=s1, pch2=s2) {
|
|
char * p1 = pch1, p2=pch2;
|
|
int c = s2.Length;
|
|
while (c-- > 0) {
|
|
if (*p1++ != *p2++)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Determines if the first string starts with the second string, ignoring case.
|
|
* Fast, non-culture aware.
|
|
*/
|
|
internal static bool StringStartsWithIgnoreCase(string s1, string s2) {
|
|
if (String.IsNullOrEmpty(s1) || String.IsNullOrEmpty(s2)) {
|
|
return false;
|
|
}
|
|
|
|
if (s2.Length > s1.Length) {
|
|
return false;
|
|
}
|
|
|
|
return 0 == string.Compare(s1, 0, s2, 0, s2.Length, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
#if !MONO
|
|
internal unsafe static void UnsafeStringCopy(string src, int srcIndex, char[] dest, int destIndex, int len) {
|
|
// We do not verify the parameters in this function, as the callers are assumed
|
|
// to have done so. This is important for users such as HttpWriter that already
|
|
// do the argument checking, and are called several times per request.
|
|
Debug.Assert(len >= 0, "len >= 0");
|
|
|
|
Debug.Assert(src != null, "src != null");
|
|
Debug.Assert(srcIndex >= 0, "srcIndex >= 0");
|
|
Debug.Assert(srcIndex + len <= src.Length, "src");
|
|
|
|
Debug.Assert(dest != null, "dest != null");
|
|
Debug.Assert(destIndex >= 0, "destIndex >= 0");
|
|
Debug.Assert(destIndex + len <= dest.Length, "dest");
|
|
|
|
int cb = len * sizeof(char);
|
|
fixed (char * pchSrc = src, pchDest = dest) {
|
|
byte* pbSrc = (byte *) (pchSrc + srcIndex);
|
|
byte* pbDest = (byte*) (pchDest + destIndex);
|
|
|
|
memcpyimpl(pbSrc, pbDest, cb);
|
|
}
|
|
}
|
|
#endif
|
|
internal static bool StringArrayEquals(string[] a, string [] b) {
|
|
if ((a == null) != (b == null)) {
|
|
return false;
|
|
}
|
|
|
|
if (a == null) {
|
|
return true;
|
|
}
|
|
|
|
int n = a.Length;
|
|
if (n != b.Length) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
if (a[i] != b[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// This is copied from String.GetHashCode. We want our own copy, because the result of
|
|
// String.GetHashCode is not guaranteed to be the same from build to build. But we want a
|
|
// stable hash, since we persist things to disk (e.g. precomp scenario). VSWhidbey 399279.
|
|
internal static int GetStringHashCode(string s) {
|
|
unsafe {
|
|
fixed (char* src = s) {
|
|
int hash1 = (5381 << 16) + 5381;
|
|
int hash2 = hash1;
|
|
|
|
// 32bit machines.
|
|
int* pint = (int*)src;
|
|
int len = s.Length;
|
|
while (len > 0) {
|
|
hash1 = ((hash1 << 5) + hash1 + (hash1 >> 27)) ^ pint[0];
|
|
if (len <= 2) {
|
|
break;
|
|
}
|
|
hash2 = ((hash2 << 5) + hash2 + (hash2 >> 27)) ^ pint[1];
|
|
pint += 2;
|
|
len -= 4;
|
|
}
|
|
return hash1 + (hash2 * 1566083941);
|
|
}
|
|
}
|
|
}
|
|
#if !MONO
|
|
internal static int GetNonRandomizedHashCode(string s, bool ignoreCase = false) {
|
|
// Preserve the default behavior when string hash randomization is off
|
|
if (!AppSettings.UseRandomizedStringHashAlgorithm) {
|
|
return ignoreCase ? StringComparer.InvariantCultureIgnoreCase.GetHashCode(s) : s.GetHashCode();
|
|
}
|
|
|
|
if (ignoreCase) {
|
|
s = s.ToLower(CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
// Use our stable hash algorithm implementation
|
|
return GetStringHashCode(s);
|
|
}
|
|
|
|
// Need StringComparer implementation. It's very expensive to port the actual BCL code here
|
|
// Instead use the default AppDomain, because it doesn't have string hash randomization enabled.
|
|
// Marshal the call to reuse the default StringComparer behavior.
|
|
// PERF isn't optimal, so apply consideration!
|
|
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "We carefully control the callers.")]
|
|
internal static int GetNonRandomizedStringComparerHashCode(string s) {
|
|
// Preserve the default behavior when string hash randomization is off
|
|
if (!AppSettings.UseRandomizedStringHashAlgorithm) {
|
|
return StringComparer.InvariantCultureIgnoreCase.GetHashCode(s);
|
|
}
|
|
|
|
ApplicationManager appManager = HostingEnvironment.GetApplicationManager();
|
|
if (appManager != null) {
|
|
// Cross AppDomain call may cause marshaling of IPrincipal to fail. So strip out the exectuion context
|
|
int hashCode = 0;
|
|
ExecutionContextUtil.RunInNullExecutionContext(delegate {
|
|
hashCode = appManager.GetNonRandomizedStringComparerHashCode(s, ignoreCase:true);
|
|
});
|
|
|
|
return hashCode;
|
|
}
|
|
|
|
// Fall back to non-compat result
|
|
return GetStringHashCode(s.ToLower(CultureInfo.InvariantCulture));
|
|
}
|
|
#endif
|
|
internal static int GetNullTerminatedByteArray(Encoding enc, string s, out byte[] bytes)
|
|
{
|
|
bytes = null;
|
|
if (s == null)
|
|
return 0;
|
|
|
|
// Encoding.GetMaxByteCount is faster than GetByteCount, but will probably allocate more
|
|
// memory than needed. Working with small short-lived strings here, so that's probably ok.
|
|
bytes = new byte[enc.GetMaxByteCount(s.Length) + 1];
|
|
return enc.GetBytes(s, 0, s.Length, bytes, 0);
|
|
}
|
|
|
|
internal unsafe static void memcpyimpl(byte* src, byte* dest, int len) {
|
|
#if !MONO
|
|
Debug.Assert(len >= 0, "Negative length in memcpyimpl!");
|
|
#endif
|
|
|
|
#if FEATURE_PAL
|
|
// Portable naive implementation
|
|
while (len-- > 0)
|
|
*dest++ = *src++;
|
|
#else
|
|
#if IA64
|
|
long dstAlign = (7 & (8 - (((long)dest) & 7))); // number of bytes to copy before dest is 8-byte aligned
|
|
|
|
while ((dstAlign > 0) && (len > 0))
|
|
{
|
|
// <
|
|
*dest++ = *src++;
|
|
|
|
len--;
|
|
dstAlign--;
|
|
}
|
|
|
|
if (len > 0)
|
|
{
|
|
long srcAlign = 8 - (((long)src) & 7);
|
|
if (srcAlign != 8)
|
|
{
|
|
if (4 == srcAlign)
|
|
{
|
|
while (len >= 4)
|
|
{
|
|
((int*)dest)[0] = ((int*)src)[0];
|
|
dest += 4;
|
|
src += 4;
|
|
len -= 4;
|
|
}
|
|
|
|
srcAlign = 2; // fall through to 2-byte copies
|
|
}
|
|
|
|
if ((2 == srcAlign) || (6 == srcAlign))
|
|
{
|
|
while (len >= 2)
|
|
{
|
|
((short*)dest)[0] = ((short*)src)[0];
|
|
dest += 2;
|
|
src += 2;
|
|
len -= 2;
|
|
}
|
|
}
|
|
|
|
while (len-- > 0)
|
|
{
|
|
*dest++ = *src++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (len >= 16)
|
|
{
|
|
do
|
|
{
|
|
((long*)dest)[0] = ((long*)src)[0];
|
|
((long*)dest)[1] = ((long*)src)[1];
|
|
dest += 16;
|
|
src += 16;
|
|
} while ((len -= 16) >= 16);
|
|
}
|
|
|
|
if(len > 0) // protection against negative len and optimization for len==16*N
|
|
{
|
|
if ((len & 8) != 0)
|
|
{
|
|
((long*)dest)[0] = ((long*)src)[0];
|
|
dest += 8;
|
|
src += 8;
|
|
}
|
|
if ((len & 4) != 0)
|
|
{
|
|
((int*)dest)[0] = ((int*)src)[0];
|
|
dest += 4;
|
|
src += 4;
|
|
}
|
|
if ((len & 2) != 0)
|
|
{
|
|
((short*)dest)[0] = ((short*)src)[0];
|
|
dest += 2;
|
|
src += 2;
|
|
}
|
|
if ((len & 1) != 0)
|
|
{
|
|
*dest++ = *src++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
// <
|
|
|
|
|
|
|
|
|
|
|
|
if (len >= 16) {
|
|
do {
|
|
#if AMD64
|
|
((long*)dest)[0] = ((long*)src)[0];
|
|
((long*)dest)[1] = ((long*)src)[1];
|
|
#else
|
|
((int*)dest)[0] = ((int*)src)[0];
|
|
((int*)dest)[1] = ((int*)src)[1];
|
|
((int*)dest)[2] = ((int*)src)[2];
|
|
((int*)dest)[3] = ((int*)src)[3];
|
|
#endif
|
|
dest += 16;
|
|
src += 16;
|
|
} while ((len -= 16) >= 16);
|
|
}
|
|
if(len > 0) // protection against negative len and optimization for len==16*N
|
|
{
|
|
if ((len & 8) != 0) {
|
|
#if AMD64
|
|
((long*)dest)[0] = ((long*)src)[0];
|
|
#else
|
|
((int*)dest)[0] = ((int*)src)[0];
|
|
((int*)dest)[1] = ((int*)src)[1];
|
|
#endif
|
|
dest += 8;
|
|
src += 8;
|
|
}
|
|
if ((len & 4) != 0) {
|
|
((int*)dest)[0] = ((int*)src)[0];
|
|
dest += 4;
|
|
src += 4;
|
|
}
|
|
if ((len & 2) != 0) {
|
|
((short*)dest)[0] = ((short*)src)[0];
|
|
dest += 2;
|
|
src += 2;
|
|
}
|
|
if ((len & 1) != 0)
|
|
*dest++ = *src++;
|
|
}
|
|
#endif
|
|
#endif // FEATURE_PAL
|
|
}
|
|
internal static string[] ObjectArrayToStringArray(object[] objectArray) {
|
|
String[] stringKeys = new String[objectArray.Length];
|
|
objectArray.CopyTo(stringKeys, 0);
|
|
return stringKeys;
|
|
}
|
|
|
|
}
|
|
}
|