Skip to content

Commit

Permalink
Avoid some unnecessary static readonly arrays (#72727)
Browse files Browse the repository at this point in the history
* Avoid some unnecessary static readonly arrays

* Address PR feedback

* Address PR feedback
  • Loading branch information
stephentoub committed Jul 24, 2022
1 parent 9f68db2 commit 214ca6d
Show file tree
Hide file tree
Showing 23 changed files with 144 additions and 259 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ internal static partial class Version
{
[LibraryImport(Libraries.Version, EntryPoint = "GetFileVersionInfoExW", StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool GetFileVersionInfoEx(
internal static unsafe partial bool GetFileVersionInfoEx(
uint dwFlags,
string lpwstrFilename,
uint dwHandle,
uint dwLen,
IntPtr lpData);
void* lpData);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ internal static partial class Version
{
[LibraryImport(Libraries.Version, EntryPoint = "VerQueryValueW", StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out uint puLen);
internal static unsafe partial bool VerQueryValue(void* pBlock, string lpSubBlock, out void* lplpBuffer, out uint puLen);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,110 +12,83 @@ private unsafe FileVersionInfo(string fileName)
{
_fileName = fileName;

uint handle; // This variable is not used, but we need an out variable.
uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(
(uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out handle);

uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out _);
if (infoSize != 0)
{
byte[] mem = new byte[infoSize];
fixed (byte* memPtr = &mem[0])
void* memPtr = NativeMemory.Alloc(infoSize);
try
{
IntPtr memIntPtr = new IntPtr((void*)memPtr);
if (Interop.Version.GetFileVersionInfoEx(
(uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | (uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
_fileName,
0U,
infoSize,
memIntPtr))
Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
_fileName,
0U,
infoSize,
memPtr))
{
uint langid = GetVarEntry(memIntPtr);
if (!GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(langid)))
{
// Some DLLs might not contain correct codepage information. In these cases we will fail during lookup.
// Explorer will take a few shots in dark by trying several specific lang-codepages
// (Explorer also randomly guesses 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE sometimes).
// We will try to simulate similar behavior here.
foreach (uint id in s_fallbackLanguageCodePages)
{
if (id != langid)
{
if (GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(id)))
{
break;
}
}
}
}
// Some dlls might not contain correct codepage information, in which case the lookup will fail. Explorer will take
// a few shots in dark. We'll simulate similar behavior by falling back to the following lang-codepages.
uint lcp = GetLanguageAndCodePage(memPtr);
_ = GetVersionInfoForCodePage(memPtr, lcp.ToString("X8")) ||
(lcp != 0x040904B0 && GetVersionInfoForCodePage(memPtr, "040904B0")) || // US English + CP_UNICODE
(lcp != 0x040904E4 && GetVersionInfoForCodePage(memPtr, "040904E4")) || // US English + CP_USASCII
(lcp != 0x04090000 && GetVersionInfoForCodePage(memPtr, "04090000")); // US English + unknown codepage
}
}
finally
{
NativeMemory.Free(memPtr);
}
}
}

// Some dlls might not contain correct codepage information,
// in which case the lookup will fail. Explorer will take
// a few shots in dark. We'll simulate similar behavior by
// falling back to the following lang-codepages:
private static readonly uint[] s_fallbackLanguageCodePages = new uint[]
{
0x040904B0, // US English + CP_UNICODE
0x040904E4, // US English + CP_USASCII
0x04090000 // US English + unknown codepage
};

private static string ConvertTo8DigitHex(uint value)
{
return value.ToString("X8");
}

private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr memPtr)
private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(void* memPtr)
{
if (Interop.Version.VerQueryValue(memPtr, "\\", out IntPtr memRef, out _))
if (Interop.Version.VerQueryValue(memPtr, "\\", out void* memRef, out _))
{
return *(Interop.Version.VS_FIXEDFILEINFO*)memRef;
}

return default;
}

private static unsafe string GetFileVersionLanguage(IntPtr memPtr)
private static unsafe string GetFileVersionLanguage(void* memPtr)
{
uint langid = GetVarEntry(memPtr) >> 16;
uint langid = GetLanguageAndCodePage(memPtr) >> 16;

const int MaxLength = 256;
char* lang = stackalloc char[MaxLength];
int charsWritten = Interop.Kernel32.VerLanguageName(langid, lang, MaxLength);
return new string(lang, 0, charsWritten);
}

private static string GetFileVersionString(IntPtr memPtr, string name)
private static unsafe string GetFileVersionString(void* memPtr, string name)
{
if (Interop.Version.VerQueryValue(memPtr, name, out IntPtr memRef, out _))
if (Interop.Version.VerQueryValue(memPtr, name, out void* memRef, out _) &&
memRef is not null)
{
if (memRef != IntPtr.Zero)
{
return Marshal.PtrToStringUni(memRef)!;
}
return Marshal.PtrToStringUni((IntPtr)memRef)!;
}

return string.Empty;
}

private static uint GetVarEntry(IntPtr memPtr)
private static unsafe uint GetLanguageAndCodePage(void* memPtr)
{
if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out IntPtr memRef, out _))
if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out void* memRef, out _))
{
return (uint)((Marshal.ReadInt16(memRef) << 16) + Marshal.ReadInt16((IntPtr)((long)memRef + 2)));
return
(uint)((*(ushort*)memRef << 16) +
*((ushort*)memRef + 1));
}

return 0x040904E4;
return 0x040904E4; // US English + CP_USASCII
}

//
// This function tries to find version information for a specific codepage.
// Returns true when version information is found.
//
private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage)
private unsafe bool GetVersionInfoForCodePage(void* memIntPtr, string codepage)
{
Span<char> stackBuffer = stackalloc char[256];

Expand Down Expand Up @@ -144,24 +117,18 @@ private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage)
_productBuild = (int)HIWORD(ffi.dwProductVersionLS);
_productPrivate = (int)LOWORD(ffi.dwProductVersionLS);

_isDebug = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
_isPatched = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
_isPrivateBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
_isPreRelease = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
_isSpecialBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;
_isDebug = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
_isPatched = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
_isPrivateBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
_isPreRelease = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
_isSpecialBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;

// fileVersion is chosen based on best guess. Other fields can be used if appropriate.
return (_fileVersion != string.Empty);
}

private static uint HIWORD(uint dword)
{
return (dword >> 16) & 0xffff;
}
private static uint HIWORD(uint dword) => (dword >> 16) & 0xffff;

private static uint LOWORD(uint dword)
{
return dword & 0xffff;
}
private static uint LOWORD(uint dword) => dword & 0xffff;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ namespace System.Net.Http
/// </summary>
internal struct MultiProxy
{
private static readonly char[] s_proxyDelimiters = { ';', ' ', '\n', '\r', '\t' };
private readonly FailedProxyCache? _failedProxyCache;
private readonly Uri[]? _uris;
private readonly string? _proxyConfig;
private readonly bool _secure;
private int _currentIndex;
private Uri? _currentUri;

public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());

private MultiProxy(FailedProxyCache? failedProxyCache, Uri[] uris)
{
_failedProxyCache = failedProxyCache;
Expand All @@ -41,6 +38,8 @@ private MultiProxy(FailedProxyCache failedProxyCache, string proxyConfig, bool s
_currentUri = null;
}

public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());

/// <summary>
/// Parses a WinHTTP proxy config into a MultiProxy instance.
/// </summary>
Expand Down Expand Up @@ -198,6 +197,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
{
const int SECURE_FLAG = 1;
const int INSECURE_FLAG = 2;
const string ProxyDelimiters = "; \n\r\t";

int wantedFlag = secure ? SECURE_FLAG : INSECURE_FLAG;
int originalLength = proxyString.Length;
Expand All @@ -206,7 +206,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
{
// Skip any delimiters.
int iter = 0;
while (iter < proxyString.Length && Array.IndexOf(s_proxyDelimiters, proxyString[iter]) >= 0)
while (iter < proxyString.Length && ProxyDelimiters.Contains(proxyString[iter]))
{
++iter;
}
Expand Down Expand Up @@ -245,7 +245,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
}

// Find the next delimiter, or end of string.
iter = proxyString.IndexOfAny(s_proxyDelimiters);
iter = proxyString.IndexOfAny(ProxyDelimiters);
if (iter < 0)
{
iter = proxyString.Length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,11 @@ public bool SendChunked
set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength;
}

// We MUST NOT send message-body when we send responses with these Status codes
private static readonly int[] s_noResponseBody = { 100, 101, 204, 205, 304 };

private static bool CanSendResponseBody(int responseCode)
{
for (int i = 0; i < s_noResponseBody.Length; i++)
{
if (responseCode == s_noResponseBody[i])
{
return false;
}
}
return true;
}

private static bool CanSendResponseBody(int responseCode) =>
// We MUST NOT send message-body when we send responses with these Status codes
responseCode is not (100 or 101 or 204 or 205 or 304);

public long ContentLength64
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,9 @@ internal HttpListenerRequest(HttpListenerContext context)
_version = HttpVersion.Version10;
}

private static readonly char[] s_separators = new char[] { ' ' };

internal void SetRequestLine(string req)
{
string[] parts = req.Split(s_separators, 3);
string[] parts = req.Split(' ', 3);
if (parts.Length != 3)
{
_context.ErrorMessage = "Invalid request line (parts).";
Expand Down
13 changes: 6 additions & 7 deletions src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ public sealed class Cookie

internal static readonly char[] PortSplitDelimiters = new char[] { ' ', ',', '\"' };
// Space (' ') should be reserved as well per RFCs, but major web browsers support it and some web sites use it - so we support it too
internal static readonly char[] ReservedToName = new char[] { '\t', '\r', '\n', '=', ';', ',' };
internal static readonly char[] ReservedToValue = new char[] { ';', ',' };
internal const string ReservedToName = "\t\r\n=;,";

private string m_comment = string.Empty; // Do not rename (binary serialization)
private Uri? m_commentUri; // Do not rename (binary serialization)
Expand Down Expand Up @@ -239,7 +238,7 @@ internal bool InternalSetName(string? value)
|| value.StartsWith('$')
|| value.StartsWith(' ')
|| value.EndsWith(' ')
|| value.IndexOfAny(ReservedToName) >= 0)
|| value.AsSpan().IndexOfAny(ReservedToName) >= 0)
{
m_name = string.Empty;
return false;
Expand Down Expand Up @@ -347,7 +346,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma
m_name.StartsWith('$') ||
m_name.StartsWith(' ') ||
m_name.EndsWith(' ') ||
m_name.IndexOfAny(ReservedToName) >= 0)
m_name.AsSpan().IndexOfAny(ReservedToName) >= 0)
{
if (shouldThrow)
{
Expand All @@ -358,7 +357,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma

// Check the value
if (m_value == null ||
(!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.IndexOfAny(ReservedToValue) >= 0))
(!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.AsSpan().IndexOfAny(';', ',') >= 0))
{
if (shouldThrow)
{
Expand All @@ -369,7 +368,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma

// Check Comment syntax
if (Comment != null && !(Comment.Length > 2 && Comment.StartsWith('\"') && Comment.EndsWith('\"'))
&& (Comment.IndexOfAny(ReservedToValue) >= 0))
&& (Comment.AsSpan().IndexOfAny(';', ',') >= 0))
{
if (shouldThrow)
{
Expand All @@ -380,7 +379,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma

// Check Path syntax
if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"'))
&& (Path.IndexOfAny(ReservedToValue) >= 0))
&& (Path.AsSpan().IndexOfAny(';', ',') != -1))
{
if (shouldThrow)
{
Expand Down
18 changes: 7 additions & 11 deletions src/libraries/System.Private.CoreLib/src/System/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,9 @@ public static partial class Convert
// Need to special case Enum because typecode will be underlying type, e.g. Int32
private static readonly Type EnumType = typeof(Enum);

internal static readonly char[] base64Table = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/', '=' };
internal const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

private const int base64LineBreakPosition = 76;
private const int Base64LineBreakPosition = 76;

#if DEBUG
static Convert()
Expand Down Expand Up @@ -2473,14 +2469,14 @@ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int
// Convert three bytes at a time to base64 notation. This will consume 4 chars.
int i;

// get a pointer to the base64Table to avoid unnecessary range checking
fixed (char* base64 = &base64Table[0])
// get a pointer to the Base64Table to avoid unnecessary range checking
fixed (char* base64 = Base64Table)
{
for (i = offset; i < calcLength; i += 3)
{
if (insertLineBreaks)
{
if (charcount == base64LineBreakPosition)
if (charcount == Base64LineBreakPosition)
{
outChars[j++] = '\r';
outChars[j++] = '\n';
Expand All @@ -2498,7 +2494,7 @@ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int
// Where we left off before
i = calcLength;

if (insertLineBreaks && (lengthmod3 != 0) && (charcount == base64LineBreakPosition))
if (insertLineBreaks && (lengthmod3 != 0) && (charcount == Base64LineBreakPosition))
{
outChars[j++] = '\r';
outChars[j++] = '\n';
Expand Down Expand Up @@ -2536,7 +2532,7 @@ private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bo

if (insertLineBreaks)
{
(uint newLines, uint remainder) = Math.DivRem(outlen, base64LineBreakPosition);
(uint newLines, uint remainder) = Math.DivRem(outlen, Base64LineBreakPosition);
if (remainder == 0)
{
--newLines;
Expand Down
Loading

0 comments on commit 214ca6d

Please sign in to comment.