Skip to content

Commit efe9663

Browse files
authored
Support the Encoding enumeration in the Encoding Providers (dotnet#37528)
* Support the Encoding enumeration in the Encoding Providers * Fix build * Fix netstandard build * Address the feedback * More Feedback * More Feedback
1 parent ed075a7 commit efe9663

15 files changed

+319
-30
lines changed

src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Diagnostics;
6+
using System.Collections.Generic;
67
using System.Diagnostics.CodeAnalysis;
78
using System.IO;
89
using System.Runtime.InteropServices;
@@ -290,8 +291,15 @@ public static Encoding GetEncoding(string name,
290291
GetEncoding(EncodingTable.GetCodePageFromName(name), encoderFallback, decoderFallback);
291292
}
292293

293-
// Return a list of all EncodingInfo objects describing all of our encodings
294-
public static EncodingInfo[] GetEncodings() => EncodingTable.GetEncodings();
294+
/// <summary>
295+
/// Get the <see cref="EncodingInfo"/> list from the runtime and all registered encoding providers
296+
/// </summary>
297+
/// <returns>The list of the <see cref="EncodingProvider"/> objects</returns>
298+
public static EncodingInfo[] GetEncodings()
299+
{
300+
Dictionary<int, EncodingInfo>? result = EncodingProvider.GetEncodingListFromProviders();
301+
return result == null ? EncodingTable.GetEncodings() : EncodingTable.GetEncodings(result);
302+
}
295303

296304
public virtual byte[] GetPreamble() => Array.Empty<byte>();
297305

src/libraries/System.Private.CoreLib/src/System/Text/EncodingInfo.cs

+49-12
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,71 @@ namespace System.Text
66
{
77
public sealed class EncodingInfo
88
{
9+
/// <summary>
10+
/// Construct an <see cref="EncodingInfo"/> object.
11+
/// </summary>
12+
/// <param name="provider">The <see cref="EncodingProvider"/> object which created this <see cref="EncodingInfo"/> object</param>
13+
/// <param name="codePage">The encoding codepage</param>
14+
/// <param name="name">The encoding name</param>
15+
/// <param name="displayName">The encoding display name</param>
16+
/// <returns></returns>
17+
public EncodingInfo(EncodingProvider provider, int codePage, string name, string displayName) : this(codePage, name, displayName)
18+
{
19+
if (name == null || displayName == null || provider == null)
20+
{
21+
throw new ArgumentNullException(name == null ? nameof(name) : (displayName == null ? nameof(displayName) : nameof(provider)));
22+
}
23+
24+
Provider = provider;
25+
}
26+
927
internal EncodingInfo(int codePage, string name, string displayName)
1028
{
1129
CodePage = codePage;
1230
Name = name;
1331
DisplayName = displayName;
1432
}
1533

34+
/// <summary>
35+
/// Get the encoding codepage number
36+
/// </summary>
37+
/// <value>The codepage integer number</value>
1638
public int CodePage { get; }
39+
40+
/// <summary>
41+
/// Get the encoding name
42+
/// </summary>
43+
/// <value>The encoding name string</value>
1744
public string Name { get; }
45+
46+
/// <summary>
47+
/// Get the encoding display name
48+
/// </summary>
49+
/// <value>The encoding display name string</value>
1850
public string DisplayName { get; }
1951

20-
public Encoding GetEncoding()
21-
{
22-
return Encoding.GetEncoding(CodePage);
23-
}
52+
/// <summary>
53+
/// Get the <see cref="Encoding"/> object match the information in the <see cref="EncodingInfo"/> object
54+
/// </summary>
55+
/// <returns>The <see cref="Encoding"/> object</returns>
56+
public Encoding GetEncoding() => Provider?.GetEncoding(CodePage) ?? Encoding.GetEncoding(CodePage);
2457

25-
public override bool Equals(object? value)
26-
{
27-
if (value is EncodingInfo that)
28-
{
29-
return this.CodePage == that.CodePage;
30-
}
31-
return false;
32-
}
58+
/// <summary>
59+
/// Compare this <see cref="EncodingInfo"/> object to other object.
60+
/// </summary>
61+
/// <param name="value">The other object to compare with this object</param>
62+
/// <returns>True if the value object is EncodingInfo object and has a codepage equals to this EncodingInfo object codepage. Otherwise, it returns False</returns>
63+
public override bool Equals(object? value) => value is EncodingInfo that && CodePage == that.CodePage;
3364

65+
/// <summary>
66+
/// Get a hashcode representing the current EncodingInfo object.
67+
/// </summary>
68+
/// <returns>The integer value representing the hash code of the EncodingInfo object.</returns>
3469
public override int GetHashCode()
3570
{
3671
return CodePage;
3772
}
73+
74+
internal EncodingProvider? Provider { get; }
3875
}
3976
}

src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs

+29-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Collections.Generic;
6+
57
namespace System.Text
68
{
79
public abstract class EncodingProvider
@@ -37,6 +39,8 @@ public EncodingProvider() { }
3739
return enc;
3840
}
3941

42+
public virtual IEnumerable<EncodingInfo> GetEncodings() => Array.Empty<EncodingInfo>();
43+
4044
internal static void AddProvider(EncodingProvider provider)
4145
{
4246
if (provider == null)
@@ -64,10 +68,10 @@ internal static void AddProvider(EncodingProvider provider)
6468

6569
internal static Encoding? GetEncodingFromProvider(int codepage)
6670
{
67-
if (s_providers == null)
71+
EncodingProvider[]? providers = s_providers;
72+
if (providers == null)
6873
return null;
6974

70-
EncodingProvider[] providers = s_providers;
7175
foreach (EncodingProvider provider in providers)
7276
{
7377
Encoding? enc = provider.GetEncoding(codepage);
@@ -78,6 +82,29 @@ internal static void AddProvider(EncodingProvider provider)
7882
return null;
7983
}
8084

85+
internal static Dictionary<int, EncodingInfo>? GetEncodingListFromProviders()
86+
{
87+
EncodingProvider[]? providers = s_providers;
88+
if (providers == null)
89+
return null;
90+
91+
Dictionary<int, EncodingInfo> result = new Dictionary<int, EncodingInfo>();
92+
93+
foreach (EncodingProvider provider in providers)
94+
{
95+
IEnumerable<EncodingInfo>? encodingInfoList = provider.GetEncodings();
96+
if (encodingInfoList != null)
97+
{
98+
foreach (EncodingInfo ei in encodingInfoList)
99+
{
100+
result.TryAdd(ei.CodePage, ei);
101+
}
102+
}
103+
}
104+
105+
return result;
106+
}
107+
81108
internal static Encoding? GetEncodingFromProvider(string encodingName)
82109
{
83110
if (s_providers == null)

src/libraries/System.Private.CoreLib/src/System/Text/EncodingTable.cs

+35-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections;
6+
using System.Collections.Generic;
67
using System.Diagnostics;
78
using System.Threading;
89

@@ -105,20 +106,49 @@ private static int InternalGetCodePageFromName(string name)
105106
// Return a list of all EncodingInfo objects describing all of our encodings
106107
internal static EncodingInfo[] GetEncodings()
107108
{
108-
EncodingInfo[] arrayEncodingInfo = new EncodingInfo[s_mappedCodePages.Length];
109+
ushort[] mappedCodePages = s_mappedCodePages;
110+
EncodingInfo[] arrayEncodingInfo = new EncodingInfo[mappedCodePages.Length];
111+
string webNames = s_webNames;
112+
int[] webNameIndices = s_webNameIndices;
109113

110-
for (int i = 0; i < s_mappedCodePages.Length; i++)
114+
for (int i = 0; i < mappedCodePages.Length; i++)
111115
{
112116
arrayEncodingInfo[i] = new EncodingInfo(
113-
s_mappedCodePages[i],
114-
s_webNames[s_webNameIndices[i]..s_webNameIndices[i + 1]],
115-
GetDisplayName(s_mappedCodePages[i], i)
117+
mappedCodePages[i],
118+
webNames[webNameIndices[i]..webNameIndices[i + 1]],
119+
GetDisplayName(mappedCodePages[i], i)
116120
);
117121
}
118122

119123
return arrayEncodingInfo;
120124
}
121125

126+
internal static EncodingInfo[] GetEncodings(Dictionary<int, EncodingInfo> encodingInfoList)
127+
{
128+
Debug.Assert(encodingInfoList != null);
129+
ushort[] mappedCodePages = s_mappedCodePages;
130+
string webNames = s_webNames;
131+
int[] webNameIndices = s_webNameIndices;
132+
133+
for (int i = 0; i < mappedCodePages.Length; i++)
134+
{
135+
if (!encodingInfoList.ContainsKey(mappedCodePages[i]))
136+
{
137+
encodingInfoList[mappedCodePages[i]] = new EncodingInfo(mappedCodePages[i], webNames[webNameIndices[i]..webNameIndices[i + 1]],
138+
GetDisplayName(mappedCodePages[i], i));
139+
}
140+
}
141+
142+
var result = new EncodingInfo[encodingInfoList.Count];
143+
int j = 0;
144+
foreach (KeyValuePair<int, EncodingInfo> pair in encodingInfoList)
145+
{
146+
result[j++] = pair.Value;
147+
}
148+
149+
return result;
150+
}
151+
122152
internal static CodePageDataItem? GetCodePageDataItem(int codePage)
123153
{
124154
if (s_codePageToCodePageData == null)

src/libraries/System.Runtime/ref/System.Runtime.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10265,7 +10265,7 @@ public static void RegisterProvider(System.Text.EncodingProvider provider) { }
1026510265
}
1026610266
public sealed partial class EncodingInfo
1026710267
{
10268-
internal EncodingInfo() { }
10268+
public EncodingInfo(System.Text.EncodingProvider provider, int codePage, string name, string displayName) {}
1026910269
public int CodePage { get { throw null; } }
1027010270
public string DisplayName { get { throw null; } }
1027110271
public string Name { get { throw null; } }
@@ -10280,6 +10280,7 @@ public EncodingProvider() { }
1028010280
public virtual System.Text.Encoding? GetEncoding(int codepage, System.Text.EncoderFallback encoderFallback, System.Text.DecoderFallback decoderFallback) { throw null; }
1028110281
public abstract System.Text.Encoding? GetEncoding(string name);
1028210282
public virtual System.Text.Encoding? GetEncoding(string name, System.Text.EncoderFallback encoderFallback, System.Text.DecoderFallback decoderFallback) { throw null; }
10283+
public virtual System.Collections.Generic.IEnumerable<System.Text.EncodingInfo> GetEncodings() { throw null; }
1028310284
}
1028410285
public enum NormalizationForm
1028510286
{
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<Nullable>enable</Nullable>
4-
<TargetFrameworks>netstandard2.0</TargetFrameworks>
4+
<TargetFrameworks>$(NetCoreAppCurrent);netstandard2.0</TargetFrameworks>
55
</PropertyGroup>
66
<ItemGroup>
77
<Compile Include="System.Text.Encoding.CodePages.cs" />
8+
<Compile Include="System.Text.Encoding.CodePages.netcoreapp.cs" Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'" />
9+
</ItemGroup>
10+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
11+
<ProjectReference Include="../../System.Runtime/ref/System.Runtime.csproj" />
812
</ItemGroup>
913
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace System.Text
6+
{
7+
public sealed partial class CodePagesEncodingProvider : System.Text.EncodingProvider
8+
{
9+
public override System.Collections.Generic.IEnumerable<System.Text.EncodingInfo> GetEncodings() { throw null; }
10+
}
11+
}

src/libraries/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
<PropertyGroup>
33
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44
<Nullable>enable</Nullable>
5-
<TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;netstandard2.0;netcoreapp2.0-Windows_NT;netstandard2.0-Windows_NT</TargetFrameworks>
5+
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-Windows_NT;netstandard2.0;netcoreapp2.0-Windows_NT;netstandard2.0-Windows_NT</TargetFrameworks>
66
<ExcludeCurrentNetCoreAppFromPackage>true</ExcludeCurrentNetCoreAppFromPackage>
77
</PropertyGroup>
88
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
99
<PropertyGroup>
1010
<!-- copy the Windows-specific implementation to net461 folder so that restore without a RID works -->
11-
<PackageTargetFramework Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetsWindows)' == 'true'">netstandard2.0;net461</PackageTargetFramework>
11+
<PackageTargetFramework Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetsWindows)' == 'true'">netstandard2.0;net461</PackageTargetFramework>
1212
</PropertyGroup>
1313
<ItemGroup>
1414
<Compile Include="Microsoft\Win32\SafeHandles\SafeAllocHHandle.cs" />
@@ -48,6 +48,13 @@
4848
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetsWindows)' != 'true' ">
4949
<Compile Include="System\Text\CodePagesEncodingProvider.Default.cs" />
5050
</ItemGroup>
51+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)' and '$(TargetsWindows)' != 'true' ">
52+
<Compile Include="System\Text\CodePagesEncodingProvider.Default.cs" />
53+
</ItemGroup>
54+
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
55+
<Compile Include="System\Text\CodePagesEncodingProvider.netcoreapp.cs" />
56+
<Compile Include="System\Text\BaseCodePageEncoding.netcoreapp.cs" />
57+
</ItemGroup>
5158
<ItemGroup>
5259
<EmbeddedResource Include="Data\codepages.nlp">
5360
<LogicalName>codepages.nlp</LogicalName>

src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace System.Text
4141
// WORD byteReplace; // 2 bytes = 48 // default replacement byte(s)
4242
// BYTE[] data; // data section
4343
// }
44-
internal abstract class BaseCodePageEncoding : EncodingNLS, ISerializable
44+
internal abstract partial class BaseCodePageEncoding : EncodingNLS, ISerializable
4545
{
4646
internal const string CODE_PAGE_DATA_FILE_NAME = "codepages.nlp";
4747

@@ -185,6 +185,7 @@ private unsafe void LoadCodePageTables()
185185
LoadManagedCodePage();
186186
}
187187

188+
188189
// Look up the code page pointer
189190
private unsafe bool FindCodePage(int codePage)
190191
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.IO;
6+
using System.Runtime.Serialization;
7+
using System.Runtime.CompilerServices;
8+
9+
namespace System.Text
10+
{
11+
internal abstract partial class BaseCodePageEncoding : EncodingNLS, ISerializable
12+
{
13+
internal static unsafe EncodingInfo [] GetEncodings(CodePagesEncodingProvider provider)
14+
{
15+
lock (s_streamLock)
16+
{
17+
s_codePagesEncodingDataStream.Seek(CODEPAGE_DATA_FILE_HEADER_SIZE, SeekOrigin.Begin);
18+
19+
int codePagesCount;
20+
fixed (byte* pBytes = &s_codePagesDataHeader[0])
21+
{
22+
CodePageDataFileHeader* pDataHeader = (CodePageDataFileHeader*)pBytes;
23+
codePagesCount = pDataHeader->CodePageCount;
24+
}
25+
26+
EncodingInfo [] encodingInfoList = new EncodingInfo[codePagesCount];
27+
28+
CodePageIndex codePageIndex = default;
29+
Span<byte> pCodePageIndex = new Span<byte>(&codePageIndex, Unsafe.SizeOf<CodePageIndex>());
30+
31+
for (int i = 0; i < codePagesCount; i++)
32+
{
33+
s_codePagesEncodingDataStream.Read(pCodePageIndex);
34+
35+
string codePageName;
36+
switch (codePageIndex.CodePage)
37+
{
38+
// Fixup some encoding names.
39+
case 950: codePageName = "big5"; break;
40+
case 10002: codePageName = "x-mac-chinesetrad"; break;
41+
case 20833: codePageName = "x-ebcdic-koreanextended"; break;
42+
default: codePageName = new string(&codePageIndex.CodePageName); break;
43+
}
44+
45+
string? resourceName = EncodingNLS.GetLocalizedEncodingNameResource(codePageIndex.CodePage);
46+
string? displayName = null;
47+
48+
if (resourceName != null && resourceName.StartsWith("Globalization_cp_", StringComparison.OrdinalIgnoreCase))
49+
{
50+
displayName = SR.GetResourceString(resourceName);
51+
}
52+
53+
encodingInfoList[i] = new EncodingInfo(provider, codePageIndex.CodePage, codePageName, displayName ?? codePageName);
54+
}
55+
56+
return encodingInfoList;
57+
}
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)