Skip to content

Commit 214ca6d

Browse files
authored
Avoid some unnecessary static readonly arrays (#72727)
* Avoid some unnecessary static readonly arrays * Address PR feedback * Address PR feedback
1 parent 9f68db2 commit 214ca6d

File tree

23 files changed

+144
-259
lines changed

23 files changed

+144
-259
lines changed

src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ internal static partial class Version
1010
{
1111
[LibraryImport(Libraries.Version, EntryPoint = "GetFileVersionInfoExW", StringMarshalling = StringMarshalling.Utf16)]
1212
[return: MarshalAs(UnmanagedType.Bool)]
13-
internal static partial bool GetFileVersionInfoEx(
13+
internal static unsafe partial bool GetFileVersionInfoEx(
1414
uint dwFlags,
1515
string lpwstrFilename,
1616
uint dwHandle,
1717
uint dwLen,
18-
IntPtr lpData);
18+
void* lpData);
1919
}
2020
}

src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ internal static partial class Version
1010
{
1111
[LibraryImport(Libraries.Version, EntryPoint = "VerQueryValueW", StringMarshalling = StringMarshalling.Utf16)]
1212
[return: MarshalAs(UnmanagedType.Bool)]
13-
internal static partial bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out uint puLen);
13+
internal static unsafe partial bool VerQueryValue(void* pBlock, string lpSubBlock, out void* lplpBuffer, out uint puLen);
1414
}
1515
}

src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs

Lines changed: 41 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -12,110 +12,83 @@ private unsafe FileVersionInfo(string fileName)
1212
{
1313
_fileName = fileName;
1414

15-
uint handle; // This variable is not used, but we need an out variable.
16-
uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(
17-
(uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out handle);
18-
15+
uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out _);
1916
if (infoSize != 0)
2017
{
21-
byte[] mem = new byte[infoSize];
22-
fixed (byte* memPtr = &mem[0])
18+
void* memPtr = NativeMemory.Alloc(infoSize);
19+
try
2320
{
24-
IntPtr memIntPtr = new IntPtr((void*)memPtr);
2521
if (Interop.Version.GetFileVersionInfoEx(
26-
(uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | (uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
27-
_fileName,
28-
0U,
29-
infoSize,
30-
memIntPtr))
22+
Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
23+
_fileName,
24+
0U,
25+
infoSize,
26+
memPtr))
3127
{
32-
uint langid = GetVarEntry(memIntPtr);
33-
if (!GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(langid)))
34-
{
35-
// Some DLLs might not contain correct codepage information. In these cases we will fail during lookup.
36-
// Explorer will take a few shots in dark by trying several specific lang-codepages
37-
// (Explorer also randomly guesses 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE sometimes).
38-
// We will try to simulate similar behavior here.
39-
foreach (uint id in s_fallbackLanguageCodePages)
40-
{
41-
if (id != langid)
42-
{
43-
if (GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(id)))
44-
{
45-
break;
46-
}
47-
}
48-
}
49-
}
28+
// Some dlls might not contain correct codepage information, in which case the lookup will fail. Explorer will take
29+
// a few shots in dark. We'll simulate similar behavior by falling back to the following lang-codepages.
30+
uint lcp = GetLanguageAndCodePage(memPtr);
31+
_ = GetVersionInfoForCodePage(memPtr, lcp.ToString("X8")) ||
32+
(lcp != 0x040904B0 && GetVersionInfoForCodePage(memPtr, "040904B0")) || // US English + CP_UNICODE
33+
(lcp != 0x040904E4 && GetVersionInfoForCodePage(memPtr, "040904E4")) || // US English + CP_USASCII
34+
(lcp != 0x04090000 && GetVersionInfoForCodePage(memPtr, "04090000")); // US English + unknown codepage
5035
}
5136
}
37+
finally
38+
{
39+
NativeMemory.Free(memPtr);
40+
}
5241
}
5342
}
5443

55-
// Some dlls might not contain correct codepage information,
56-
// in which case the lookup will fail. Explorer will take
57-
// a few shots in dark. We'll simulate similar behavior by
58-
// falling back to the following lang-codepages:
59-
private static readonly uint[] s_fallbackLanguageCodePages = new uint[]
60-
{
61-
0x040904B0, // US English + CP_UNICODE
62-
0x040904E4, // US English + CP_USASCII
63-
0x04090000 // US English + unknown codepage
64-
};
65-
66-
private static string ConvertTo8DigitHex(uint value)
67-
{
68-
return value.ToString("X8");
69-
}
70-
71-
private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr memPtr)
44+
private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(void* memPtr)
7245
{
73-
if (Interop.Version.VerQueryValue(memPtr, "\\", out IntPtr memRef, out _))
46+
if (Interop.Version.VerQueryValue(memPtr, "\\", out void* memRef, out _))
7447
{
7548
return *(Interop.Version.VS_FIXEDFILEINFO*)memRef;
7649
}
7750

7851
return default;
7952
}
8053

81-
private static unsafe string GetFileVersionLanguage(IntPtr memPtr)
54+
private static unsafe string GetFileVersionLanguage(void* memPtr)
8255
{
83-
uint langid = GetVarEntry(memPtr) >> 16;
56+
uint langid = GetLanguageAndCodePage(memPtr) >> 16;
8457

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

91-
private static string GetFileVersionString(IntPtr memPtr, string name)
64+
private static unsafe string GetFileVersionString(void* memPtr, string name)
9265
{
93-
if (Interop.Version.VerQueryValue(memPtr, name, out IntPtr memRef, out _))
66+
if (Interop.Version.VerQueryValue(memPtr, name, out void* memRef, out _) &&
67+
memRef is not null)
9468
{
95-
if (memRef != IntPtr.Zero)
96-
{
97-
return Marshal.PtrToStringUni(memRef)!;
98-
}
69+
return Marshal.PtrToStringUni((IntPtr)memRef)!;
9970
}
10071

10172
return string.Empty;
10273
}
10374

104-
private static uint GetVarEntry(IntPtr memPtr)
75+
private static unsafe uint GetLanguageAndCodePage(void* memPtr)
10576
{
106-
if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out IntPtr memRef, out _))
77+
if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out void* memRef, out _))
10778
{
108-
return (uint)((Marshal.ReadInt16(memRef) << 16) + Marshal.ReadInt16((IntPtr)((long)memRef + 2)));
79+
return
80+
(uint)((*(ushort*)memRef << 16) +
81+
*((ushort*)memRef + 1));
10982
}
11083

111-
return 0x040904E4;
84+
return 0x040904E4; // US English + CP_USASCII
11285
}
11386

11487
//
11588
// This function tries to find version information for a specific codepage.
11689
// Returns true when version information is found.
11790
//
118-
private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage)
91+
private unsafe bool GetVersionInfoForCodePage(void* memIntPtr, string codepage)
11992
{
12093
Span<char> stackBuffer = stackalloc char[256];
12194

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

147-
_isDebug = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
148-
_isPatched = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
149-
_isPrivateBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
150-
_isPreRelease = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
151-
_isSpecialBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;
120+
_isDebug = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
121+
_isPatched = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
122+
_isPrivateBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
123+
_isPreRelease = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
124+
_isSpecialBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;
152125

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

157-
private static uint HIWORD(uint dword)
158-
{
159-
return (dword >> 16) & 0xffff;
160-
}
130+
private static uint HIWORD(uint dword) => (dword >> 16) & 0xffff;
161131

162-
private static uint LOWORD(uint dword)
163-
{
164-
return dword & 0xffff;
165-
}
132+
private static uint LOWORD(uint dword) => dword & 0xffff;
166133
}
167134
}

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,13 @@ namespace System.Net.Http
1111
/// </summary>
1212
internal struct MultiProxy
1313
{
14-
private static readonly char[] s_proxyDelimiters = { ';', ' ', '\n', '\r', '\t' };
1514
private readonly FailedProxyCache? _failedProxyCache;
1615
private readonly Uri[]? _uris;
1716
private readonly string? _proxyConfig;
1817
private readonly bool _secure;
1918
private int _currentIndex;
2019
private Uri? _currentUri;
2120

22-
public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());
23-
2421
private MultiProxy(FailedProxyCache? failedProxyCache, Uri[] uris)
2522
{
2623
_failedProxyCache = failedProxyCache;
@@ -41,6 +38,8 @@ private MultiProxy(FailedProxyCache failedProxyCache, string proxyConfig, bool s
4138
_currentUri = null;
4239
}
4340

41+
public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());
42+
4443
/// <summary>
4544
/// Parses a WinHTTP proxy config into a MultiProxy instance.
4645
/// </summary>
@@ -198,6 +197,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
198197
{
199198
const int SECURE_FLAG = 1;
200199
const int INSECURE_FLAG = 2;
200+
const string ProxyDelimiters = "; \n\r\t";
201201

202202
int wantedFlag = secure ? SECURE_FLAG : INSECURE_FLAG;
203203
int originalLength = proxyString.Length;
@@ -206,7 +206,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
206206
{
207207
// Skip any delimiters.
208208
int iter = 0;
209-
while (iter < proxyString.Length && Array.IndexOf(s_proxyDelimiters, proxyString[iter]) >= 0)
209+
while (iter < proxyString.Length && ProxyDelimiters.Contains(proxyString[iter]))
210210
{
211211
++iter;
212212
}
@@ -245,7 +245,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan<char> proxyString, bool
245245
}
246246

247247
// Find the next delimiter, or end of string.
248-
iter = proxyString.IndexOfAny(s_proxyDelimiters);
248+
iter = proxyString.IndexOfAny(ProxyDelimiters);
249249
if (iter < 0)
250250
{
251251
iter = proxyString.Length;

src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,11 @@ public bool SendChunked
7979
set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength;
8080
}
8181

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

85-
private static bool CanSendResponseBody(int responseCode)
86-
{
87-
for (int i = 0; i < s_noResponseBody.Length; i++)
88-
{
89-
if (responseCode == s_noResponseBody[i])
90-
{
91-
return false;
92-
}
93-
}
94-
return true;
95-
}
83+
84+
private static bool CanSendResponseBody(int responseCode) =>
85+
// We MUST NOT send message-body when we send responses with these Status codes
86+
responseCode is not (100 or 101 or 204 or 205 or 304);
9687

9788
public long ContentLength64
9889
{

src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,9 @@ internal HttpListenerRequest(HttpListenerContext context)
7373
_version = HttpVersion.Version10;
7474
}
7575

76-
private static readonly char[] s_separators = new char[] { ' ' };
77-
7876
internal void SetRequestLine(string req)
7977
{
80-
string[] parts = req.Split(s_separators, 3);
78+
string[] parts = req.Split(' ', 3);
8179
if (parts.Length != 3)
8280
{
8381
_context.ErrorMessage = "Invalid request line (parts).";

src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ public sealed class Cookie
4141

4242
internal static readonly char[] PortSplitDelimiters = new char[] { ' ', ',', '\"' };
4343
// 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
44-
internal static readonly char[] ReservedToName = new char[] { '\t', '\r', '\n', '=', ';', ',' };
45-
internal static readonly char[] ReservedToValue = new char[] { ';', ',' };
44+
internal const string ReservedToName = "\t\r\n=;,";
4645

4746
private string m_comment = string.Empty; // Do not rename (binary serialization)
4847
private Uri? m_commentUri; // Do not rename (binary serialization)
@@ -239,7 +238,7 @@ internal bool InternalSetName(string? value)
239238
|| value.StartsWith('$')
240239
|| value.StartsWith(' ')
241240
|| value.EndsWith(' ')
242-
|| value.IndexOfAny(ReservedToName) >= 0)
241+
|| value.AsSpan().IndexOfAny(ReservedToName) >= 0)
243242
{
244243
m_name = string.Empty;
245244
return false;
@@ -347,7 +346,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma
347346
m_name.StartsWith('$') ||
348347
m_name.StartsWith(' ') ||
349348
m_name.EndsWith(' ') ||
350-
m_name.IndexOfAny(ReservedToName) >= 0)
349+
m_name.AsSpan().IndexOfAny(ReservedToName) >= 0)
351350
{
352351
if (shouldThrow)
353352
{
@@ -358,7 +357,7 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma
358357

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

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

381380
// Check Path syntax
382381
if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"'))
383-
&& (Path.IndexOfAny(ReservedToValue) >= 0))
382+
&& (Path.AsSpan().IndexOfAny(';', ',') != -1))
384383
{
385384
if (shouldThrow)
386385
{

src/libraries/System.Private.CoreLib/src/System/Convert.cs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,9 @@ public static partial class Convert
101101
// Need to special case Enum because typecode will be underlying type, e.g. Int32
102102
private static readonly Type EnumType = typeof(Enum);
103103

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

110-
private const int base64LineBreakPosition = 76;
106+
private const int Base64LineBreakPosition = 76;
111107

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

2476-
// get a pointer to the base64Table to avoid unnecessary range checking
2477-
fixed (char* base64 = &base64Table[0])
2472+
// get a pointer to the Base64Table to avoid unnecessary range checking
2473+
fixed (char* base64 = Base64Table)
24782474
{
24792475
for (i = offset; i < calcLength; i += 3)
24802476
{
24812477
if (insertLineBreaks)
24822478
{
2483-
if (charcount == base64LineBreakPosition)
2479+
if (charcount == Base64LineBreakPosition)
24842480
{
24852481
outChars[j++] = '\r';
24862482
outChars[j++] = '\n';
@@ -2498,7 +2494,7 @@ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int
24982494
// Where we left off before
24992495
i = calcLength;
25002496

2501-
if (insertLineBreaks && (lengthmod3 != 0) && (charcount == base64LineBreakPosition))
2497+
if (insertLineBreaks && (lengthmod3 != 0) && (charcount == Base64LineBreakPosition))
25022498
{
25032499
outChars[j++] = '\r';
25042500
outChars[j++] = '\n';
@@ -2536,7 +2532,7 @@ private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bo
25362532

25372533
if (insertLineBreaks)
25382534
{
2539-
(uint newLines, uint remainder) = Math.DivRem(outlen, base64LineBreakPosition);
2535+
(uint newLines, uint remainder) = Math.DivRem(outlen, Base64LineBreakPosition);
25402536
if (remainder == 0)
25412537
{
25422538
--newLines;

0 commit comments

Comments
 (0)