Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit a9b57bd

Browse files
authored
Remove remaining StringBuilder marshaling use from Corelib (#21120)
* Remove remaining StringBuilder marshaling use from Corelib - Unix globalization functions were using StringBuilder. Replaced them with stackallocs, as the max sizes were all relatively small. - Registry methods were declared to use StringBuilder, but these weren't actually used by corelib, and in fact, corefx isn't using StringBuilder with them either. I've changed the definitions for now to use char[] instead (all the call sites are passing in null), and I'll fix up some corefx usage separately. - Resource-related functions were using StringBuilder, and have been replaced with stackallocs, given reasonably small max buffer sizes. - ExpandEnvironmentVariables was using a StringBuilder, but it's rewritten equivalent code in corefx is not. For now I've copied the corefx function over to coreclr to replace the different implementation, but we can look at subsequently consolidating them into one. * Address PR feedback
1 parent 5a0af05 commit a9b57bd

File tree

17 files changed

+115
-138
lines changed

17 files changed

+115
-138
lines changed

src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Calendar.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal delegate void EnumCalendarInfoCallback(
1919
internal static extern int GetCalendars(string localeName, CalendarId[] calendars, int calendarsCapacity);
2020

2121
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetCalendarInfo")]
22-
internal static extern ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, [Out] StringBuilder result, int resultCapacity);
22+
internal static extern unsafe ResultCode GetCalendarInfo(string localeName, CalendarId calendarId, CalendarDataType calendarDataType, char* result, int resultCapacity);
2323

2424
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_EnumCalendarInfo")]
2525
internal static extern bool EnumCalendarInfo(EnumCalendarInfoCallback callback, string localeName, CalendarId calendarId, CalendarDataType calendarDataType, IntPtr context);

src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Locale.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,36 @@
44

55
using System;
66
using System.Runtime.InteropServices;
7-
using System.Text;
87

98
internal static partial class Interop
109
{
1110
internal static partial class Globalization
1211
{
1312
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleName")]
1413
[return: MarshalAs(UnmanagedType.Bool)]
15-
internal static extern unsafe bool GetLocaleName(string localeName, [Out] StringBuilder value, int valueLength);
14+
internal static extern unsafe bool GetLocaleName(string localeName, char* value, int valueLength);
1615

1716
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoString")]
1817
[return: MarshalAs(UnmanagedType.Bool)]
19-
internal static extern unsafe bool GetLocaleInfoString(string localeName, uint localeStringData, [Out] StringBuilder value, int valueLength);
18+
internal static extern unsafe bool GetLocaleInfoString(string localeName, uint localeStringData, char* value, int valueLength);
2019

2120
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetDefaultLocaleName")]
2221
[return: MarshalAs(UnmanagedType.Bool)]
23-
internal static extern unsafe bool GetDefaultLocaleName([Out] StringBuilder value, int valueLength);
22+
internal static extern unsafe bool GetDefaultLocaleName(char* value, int valueLength);
2423

2524
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleTimeFormat")]
2625
[return: MarshalAs(UnmanagedType.Bool)]
27-
internal static extern unsafe bool GetLocaleTimeFormat(string localeName, bool shortFormat, [Out] StringBuilder value, int valueLength);
26+
internal static extern unsafe bool GetLocaleTimeFormat(string localeName, bool shortFormat, char* value, int valueLength);
2827

2928
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoInt")]
3029
[return: MarshalAs(UnmanagedType.Bool)]
31-
internal static extern unsafe bool GetLocaleInfoInt(string localeName, uint localeNumberData, ref int value);
30+
internal static extern bool GetLocaleInfoInt(string localeName, uint localeNumberData, ref int value);
3231

3332
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocaleInfoGroupingSizes")]
3433
[return: MarshalAs(UnmanagedType.Bool)]
35-
internal static extern unsafe bool GetLocaleInfoGroupingSizes(string localeName, uint localeGroupingData, ref int primaryGroupSize, ref int secondaryGroupSize);
34+
internal static extern bool GetLocaleInfoGroupingSizes(string localeName, uint localeGroupingData, ref int primaryGroupSize, ref int secondaryGroupSize);
3635

3736
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetLocales")]
38-
internal static extern unsafe int GetLocales([Out] char[] value, int valueLength);
37+
internal static extern int GetLocales([Out] char[] value, int valueLength);
3938
}
4039
}

src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.TimeZoneInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ internal enum TimeZoneDisplayNameType
1818
}
1919

2020
[DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_GetTimeZoneDisplayName")]
21-
internal static extern ResultCode GetTimeZoneDisplayName(
21+
internal static extern unsafe ResultCode GetTimeZoneDisplayName(
2222
string localeName,
2323
string timeZoneId,
2424
TimeZoneDisplayNameType type,
25-
[Out] StringBuilder result,
25+
char* result,
2626
int resultLength);
2727
}
2828
}

src/System.Private.CoreLib/shared/Interop/Unix/System.Globalization.Native/Interop.Utils.cs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Diagnostics;
7+
using System.Buffers;
68
using System.Text;
79

810
internal static partial class Interop
@@ -13,39 +15,33 @@ internal static partial class Interop
1315
/// increasing buffer until the size is big enough.
1416
/// </summary>
1517
internal static bool CallStringMethod<TArg1, TArg2, TArg3>(
16-
Func<TArg1, TArg2, TArg3, StringBuilder, Interop.Globalization.ResultCode> interopCall,
17-
TArg1 arg1,
18-
TArg2 arg2,
19-
TArg3 arg3,
18+
SpanFunc<char, TArg1, TArg2, TArg3, Interop.Globalization.ResultCode> interopCall,
19+
TArg1 arg1, TArg2 arg2, TArg3 arg3,
2020
out string result)
2121
{
22-
const int initialStringSize = 80;
23-
const int maxDoubleAttempts = 5;
22+
const int InitialSize = 256; // arbitrary stack allocation size
23+
const int MaxHeapSize = 1280; // max from previous version of the code, starting at 80 and doubling four times
2424

25-
StringBuilder stringBuilder = StringBuilderCache.Acquire(initialStringSize);
25+
Span<char> buffer = stackalloc char[InitialSize];
26+
Interop.Globalization.ResultCode resultCode = interopCall(buffer, arg1, arg2, arg3);
2627

27-
for (int i = 0; i < maxDoubleAttempts; i++)
28+
if (resultCode == Interop.Globalization.ResultCode.Success)
2829
{
29-
Interop.Globalization.ResultCode resultCode = interopCall(arg1, arg2, arg3, stringBuilder);
30+
result = buffer.Slice(0, buffer.IndexOf('\0')).ToString();
31+
return true;
32+
}
3033

31-
if (resultCode == Interop.Globalization.ResultCode.Success)
34+
if (resultCode == Interop.Globalization.ResultCode.InsufficentBuffer)
35+
{
36+
// Increase the string size and try again
37+
buffer = new char[MaxHeapSize];
38+
if (interopCall(buffer, arg1, arg2, arg3) == Interop.Globalization.ResultCode.Success)
3239
{
33-
result = StringBuilderCache.GetStringAndRelease(stringBuilder);
40+
result = buffer.Slice(0, buffer.IndexOf('\0')).ToString();
3441
return true;
3542
}
36-
else if (resultCode == Interop.Globalization.ResultCode.InsufficentBuffer)
37-
{
38-
// increase the string size and loop
39-
stringBuilder.EnsureCapacity(stringBuilder.Capacity * 2);
40-
}
41-
else
42-
{
43-
// if there is an unknown error, don't proceed
44-
break;
45-
}
4643
}
4744

48-
StringBuilderCache.Release(stringBuilder);
4945
result = null;
5046
return false;
5147
}

src/System.Private.CoreLib/shared/Interop/Windows/Advapi32/Interop.RegEnumKeyEx.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal static extern unsafe int RegEnumKeyEx(
2222
char[] lpName,
2323
ref int lpcbName,
2424
int[] lpReserved,
25-
[Out]StringBuilder lpClass,
25+
[Out] char[] lpClass,
2626
int[] lpcbClass,
2727
long[] lpftLastWriteTime);
2828
}

src/System.Private.CoreLib/shared/Interop/Windows/Advapi32/Interop.RegQueryInfoKey.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ internal partial class Advapi32
1818
[DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, BestFitMapping = false, EntryPoint = "RegQueryInfoKeyW")]
1919
internal static extern int RegQueryInfoKey(
2020
SafeRegistryHandle hKey,
21-
[Out]StringBuilder lpClass,
21+
[Out] char[] lpClass,
2222
int[] lpcbClass,
2323
IntPtr lpReserved_MustBeZero,
2424
ref int lpcSubKeys,

src/System.Private.CoreLib/shared/Interop/Windows/Advapi32/Interop.RegQueryValueEx.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,5 @@ internal static extern int RegQueryValueEx(
5050
ref int lpType,
5151
[Out] char[] lpData,
5252
ref int lpcbData);
53-
54-
[DllImport(Libraries.Advapi32, CharSet = CharSet.Unicode, BestFitMapping = false, EntryPoint = "RegQueryValueExW")]
55-
internal static extern int RegQueryValueEx(
56-
SafeRegistryHandle hKey,
57-
string lpValueName,
58-
int[] lpReserved,
59-
ref int lpType,
60-
[Out]StringBuilder lpData,
61-
ref int lpcbData);
6253
}
6354
}

src/System.Private.CoreLib/shared/Interop/Windows/Kernel32/Interop.MUI.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
using System;
66
using System.Runtime.InteropServices;
7-
using System.Text;
87

98
internal static partial class Interop
109
{
@@ -13,6 +12,6 @@ internal static partial class Kernel32
1312
internal const uint MUI_PREFERRED_UI_LANGUAGES = 0x10;
1413

1514
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
16-
internal static extern bool GetFileMUIPath(uint flags, string filePath, [Out] StringBuilder language, ref int languageLength, [Out] StringBuilder fileMuiPath, ref int fileMuiPathLength, ref long enumerator);
15+
internal static extern unsafe bool GetFileMUIPath(uint dwFlags, string pcwszFilePath, char* pwszLanguage, ref int pcchLanguage, char* pwszFileMUIPath, ref int pcchFileMUIPath, ref long pululEnumerator);
1716
}
1817
}

src/System.Private.CoreLib/shared/Interop/Windows/User32/Interop.LoadString.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Runtime.InteropServices;
6-
using System.Text;
76
using Microsoft.Win32.SafeHandles;
87

98
internal partial class Interop
109
{
1110
internal partial class User32
1211
{
1312
[DllImport(Libraries.User32, SetLastError = true, EntryPoint = "LoadStringW", CharSet = CharSet.Unicode)]
14-
internal static extern int LoadString(SafeLibraryHandle handle, int id, [Out] StringBuilder buffer, int bufferLength);
13+
internal static unsafe extern int LoadString(SafeLibraryHandle hInstance, uint uID, char* lpBuffer, int cchBufferMax);
1514
}
1615
}

src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,6 @@
893893
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegFlushKey.cs" />
894894
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegistryConstants.cs" />
895895
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegOpenKeyEx.cs" />
896-
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegQueryInfoKey.cs" />
897896
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegQueryValueEx.cs" />
898897
<Compile Include="$(MSBuildThisFileDirectory)Interop\Windows\Advapi32\Interop.RegSetValueEx.cs" />
899898
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />

0 commit comments

Comments
 (0)