diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs b/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs index 3f0d0350610bad..08257200289838 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; @@ -290,8 +291,15 @@ public static Encoding GetEncoding(string name, GetEncoding(EncodingTable.GetCodePageFromName(name), encoderFallback, decoderFallback); } - // Return a list of all EncodingInfo objects describing all of our encodings - public static EncodingInfo[] GetEncodings() => EncodingTable.GetEncodings(); + /// + /// Get the list from the runtime and all registered encoding providers + /// + /// The list of the objects + public static EncodingInfo[] GetEncodings() + { + Dictionary? result = EncodingProvider.GetEncodingListFromProviders(); + return result == null ? EncodingTable.GetEncodings() : EncodingTable.GetEncodings(result); + } public virtual byte[] GetPreamble() => Array.Empty(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingInfo.cs index febecfa6651f6b..b1ce0cd5c62672 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingInfo.cs @@ -6,6 +6,24 @@ namespace System.Text { public sealed class EncodingInfo { + /// + /// Construct an object. + /// + /// The object which created this object + /// The encoding codepage + /// The encoding name + /// The encoding display name + /// + public EncodingInfo(EncodingProvider provider, int codePage, string name, string displayName) : this(codePage, name, displayName) + { + if (name == null || displayName == null || provider == null) + { + throw new ArgumentNullException(name == null ? nameof(name) : (displayName == null ? nameof(displayName) : nameof(provider))); + } + + Provider = provider; + } + internal EncodingInfo(int codePage, string name, string displayName) { CodePage = codePage; @@ -13,27 +31,46 @@ internal EncodingInfo(int codePage, string name, string displayName) DisplayName = displayName; } + /// + /// Get the encoding codepage number + /// + /// The codepage integer number public int CodePage { get; } + + /// + /// Get the encoding name + /// + /// The encoding name string public string Name { get; } + + /// + /// Get the encoding display name + /// + /// The encoding display name string public string DisplayName { get; } - public Encoding GetEncoding() - { - return Encoding.GetEncoding(CodePage); - } + /// + /// Get the object match the information in the object + /// + /// The object + public Encoding GetEncoding() => Provider?.GetEncoding(CodePage) ?? Encoding.GetEncoding(CodePage); - public override bool Equals(object? value) - { - if (value is EncodingInfo that) - { - return this.CodePage == that.CodePage; - } - return false; - } + /// + /// Compare this object to other object. + /// + /// The other object to compare with this object + /// True if the value object is EncodingInfo object and has a codepage equals to this EncodingInfo object codepage. Otherwise, it returns False + public override bool Equals(object? value) => value is EncodingInfo that && CodePage == that.CodePage; + /// + /// Get a hashcode representing the current EncodingInfo object. + /// + /// The integer value representing the hash code of the EncodingInfo object. public override int GetHashCode() { return CodePage; } + + internal EncodingProvider? Provider { get; } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs index ac7fc541f60105..340329e25316e9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingProvider.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Generic; + namespace System.Text { public abstract class EncodingProvider @@ -37,6 +39,8 @@ public EncodingProvider() { } return enc; } + public virtual IEnumerable GetEncodings() => Array.Empty(); + internal static void AddProvider(EncodingProvider provider) { if (provider == null) @@ -64,10 +68,10 @@ internal static void AddProvider(EncodingProvider provider) internal static Encoding? GetEncodingFromProvider(int codepage) { - if (s_providers == null) + EncodingProvider[]? providers = s_providers; + if (providers == null) return null; - EncodingProvider[] providers = s_providers; foreach (EncodingProvider provider in providers) { Encoding? enc = provider.GetEncoding(codepage); @@ -78,6 +82,29 @@ internal static void AddProvider(EncodingProvider provider) return null; } + internal static Dictionary? GetEncodingListFromProviders() + { + EncodingProvider[]? providers = s_providers; + if (providers == null) + return null; + + Dictionary result = new Dictionary(); + + foreach (EncodingProvider provider in providers) + { + IEnumerable? encodingInfoList = provider.GetEncodings(); + if (encodingInfoList != null) + { + foreach (EncodingInfo ei in encodingInfoList) + { + result.TryAdd(ei.CodePage, ei); + } + } + } + + return result; + } + internal static Encoding? GetEncodingFromProvider(string encodingName) { if (s_providers == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingTable.cs b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingTable.cs index cc69f793d6e8f7..40904f1cbc2c38 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/EncodingTable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/EncodingTable.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -105,20 +106,49 @@ private static int InternalGetCodePageFromName(string name) // Return a list of all EncodingInfo objects describing all of our encodings internal static EncodingInfo[] GetEncodings() { - EncodingInfo[] arrayEncodingInfo = new EncodingInfo[s_mappedCodePages.Length]; + ushort[] mappedCodePages = s_mappedCodePages; + EncodingInfo[] arrayEncodingInfo = new EncodingInfo[mappedCodePages.Length]; + string webNames = s_webNames; + int[] webNameIndices = s_webNameIndices; - for (int i = 0; i < s_mappedCodePages.Length; i++) + for (int i = 0; i < mappedCodePages.Length; i++) { arrayEncodingInfo[i] = new EncodingInfo( - s_mappedCodePages[i], - s_webNames[s_webNameIndices[i]..s_webNameIndices[i + 1]], - GetDisplayName(s_mappedCodePages[i], i) + mappedCodePages[i], + webNames[webNameIndices[i]..webNameIndices[i + 1]], + GetDisplayName(mappedCodePages[i], i) ); } return arrayEncodingInfo; } + internal static EncodingInfo[] GetEncodings(Dictionary encodingInfoList) + { + Debug.Assert(encodingInfoList != null); + ushort[] mappedCodePages = s_mappedCodePages; + string webNames = s_webNames; + int[] webNameIndices = s_webNameIndices; + + for (int i = 0; i < mappedCodePages.Length; i++) + { + if (!encodingInfoList.ContainsKey(mappedCodePages[i])) + { + encodingInfoList[mappedCodePages[i]] = new EncodingInfo(mappedCodePages[i], webNames[webNameIndices[i]..webNameIndices[i + 1]], + GetDisplayName(mappedCodePages[i], i)); + } + } + + var result = new EncodingInfo[encodingInfoList.Count]; + int j = 0; + foreach (KeyValuePair pair in encodingInfoList) + { + result[j++] = pair.Value; + } + + return result; + } + internal static CodePageDataItem? GetCodePageDataItem(int codePage) { if (s_codePageToCodePageData == null) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 4c39802764aae4..7a2d7b4f6ce1b2 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -10262,7 +10262,7 @@ public static void RegisterProvider(System.Text.EncodingProvider provider) { } } public sealed partial class EncodingInfo { - internal EncodingInfo() { } + public EncodingInfo(System.Text.EncodingProvider provider, int codePage, string name, string displayName) {} public int CodePage { get { throw null; } } public string DisplayName { get { throw null; } } public string Name { get { throw null; } } @@ -10277,6 +10277,7 @@ public EncodingProvider() { } public virtual System.Text.Encoding? GetEncoding(int codepage, System.Text.EncoderFallback encoderFallback, System.Text.DecoderFallback decoderFallback) { throw null; } public abstract System.Text.Encoding? GetEncoding(string name); public virtual System.Text.Encoding? GetEncoding(string name, System.Text.EncoderFallback encoderFallback, System.Text.DecoderFallback decoderFallback) { throw null; } + public virtual System.Collections.Generic.IEnumerable GetEncodings() { throw null; } } public enum NormalizationForm { diff --git a/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.csproj b/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.csproj index 23981a702056bc..c29c17eb07596c 100644 --- a/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.csproj +++ b/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.csproj @@ -1,9 +1,13 @@ enable - netstandard2.0 + $(NetCoreAppCurrent);netstandard2.0 + + + + \ No newline at end of file diff --git a/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.netcoreapp.cs b/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.netcoreapp.cs new file mode 100644 index 00000000000000..06b46e94552231 --- /dev/null +++ b/src/libraries/System.Text.Encoding.CodePages/ref/System.Text.Encoding.CodePages.netcoreapp.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Text +{ + public sealed partial class CodePagesEncodingProvider : System.Text.EncodingProvider + { + public override System.Collections.Generic.IEnumerable GetEncodings() { throw null; } + } +} diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj b/src/libraries/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj index 778cc8effa9e53..88407f0f334d26 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj +++ b/src/libraries/System.Text.Encoding.CodePages/src/System.Text.Encoding.CodePages.csproj @@ -2,13 +2,13 @@ true enable - $(NetCoreAppCurrent)-Windows_NT;netstandard2.0;netcoreapp2.0-Windows_NT;netstandard2.0-Windows_NT + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Windows_NT;netstandard2.0;netcoreapp2.0-Windows_NT;netstandard2.0-Windows_NT true - netstandard2.0;net461 + netstandard2.0;net461 @@ -48,6 +48,13 @@ + + + + + + + codepages.nlp diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs index 7b4f57b5776287..5d26bfff272e9b 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.cs @@ -41,7 +41,7 @@ namespace System.Text // WORD byteReplace; // 2 bytes = 48 // default replacement byte(s) // BYTE[] data; // data section // } - internal abstract class BaseCodePageEncoding : EncodingNLS, ISerializable + internal abstract partial class BaseCodePageEncoding : EncodingNLS, ISerializable { internal const string CODE_PAGE_DATA_FILE_NAME = "codepages.nlp"; @@ -185,6 +185,7 @@ private unsafe void LoadCodePageTables() LoadManagedCodePage(); } + // Look up the code page pointer private unsafe bool FindCodePage(int codePage) { diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs new file mode 100644 index 00000000000000..9c416ac0ef4fba --- /dev/null +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/BaseCodePageEncoding.netcoreapp.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.CompilerServices; + +namespace System.Text +{ + internal abstract partial class BaseCodePageEncoding : EncodingNLS, ISerializable + { + internal static unsafe EncodingInfo [] GetEncodings(CodePagesEncodingProvider provider) + { + lock (s_streamLock) + { + s_codePagesEncodingDataStream.Seek(CODEPAGE_DATA_FILE_HEADER_SIZE, SeekOrigin.Begin); + + int codePagesCount; + fixed (byte* pBytes = &s_codePagesDataHeader[0]) + { + CodePageDataFileHeader* pDataHeader = (CodePageDataFileHeader*)pBytes; + codePagesCount = pDataHeader->CodePageCount; + } + + EncodingInfo [] encodingInfoList = new EncodingInfo[codePagesCount]; + + CodePageIndex codePageIndex = default; + Span pCodePageIndex = new Span(&codePageIndex, Unsafe.SizeOf()); + + for (int i = 0; i < codePagesCount; i++) + { + s_codePagesEncodingDataStream.Read(pCodePageIndex); + + string codePageName; + switch (codePageIndex.CodePage) + { + // Fixup some encoding names. + case 950: codePageName = "big5"; break; + case 10002: codePageName = "x-mac-chinesetrad"; break; + case 20833: codePageName = "x-ebcdic-koreanextended"; break; + default: codePageName = new string(&codePageIndex.CodePageName); break; + } + + string? resourceName = EncodingNLS.GetLocalizedEncodingNameResource(codePageIndex.CodePage); + string? displayName = null; + + if (resourceName != null && resourceName.StartsWith("Globalization_cp_", StringComparison.OrdinalIgnoreCase)) + { + displayName = SR.GetResourceString(resourceName); + } + + encodingInfoList[i] = new EncodingInfo(provider, codePageIndex.CodePage, codePageName, displayName ?? codePageName); + } + + return encodingInfoList; + } + } + } +} diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.netcoreapp.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.netcoreapp.cs new file mode 100644 index 00000000000000..2260eee7891766 --- /dev/null +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/CodePagesEncodingProvider.netcoreapp.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace System.Text +{ + public sealed partial class CodePagesEncodingProvider : EncodingProvider + { + public override System.Collections.Generic.IEnumerable GetEncodings() => BaseCodePageEncoding.GetEncodings(this); + } +} diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs index 713c3201a220fa..0bcfef5d209101 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingNLS.cs @@ -392,7 +392,7 @@ public override string EncodingName } } - private static string? GetLocalizedEncodingNameResource(int codePage) => + internal static string? GetLocalizedEncodingNameResource(int codePage) => codePage switch { 37 => SR.Globalization_cp_37, @@ -549,12 +549,34 @@ public override string WebName _webName = EncodingTable.GetWebNameFromCodePage(CodePage); if (_webName == null) { - throw new NotSupportedException( - SR.Format(SR.NotSupported_NoCodepageData, CodePage)); + throw new NotSupportedException(SR.Format(SR.NotSupported_NoCodepageData, CodePage)); } } return _webName; } } + + public override string HeaderName => + CodePage switch + { + 932 => "iso-2022-jp", + 50221 => "iso-2022-jp", + 50225 => "euc-kr", + _ => WebName, + }; + + public override string BodyName => + CodePage switch + { + 932 => "iso-2022-jp", + 1250 => "iso-8859-2", + 1251 => "koi8-r", + 1252 => "iso-8859-1", + 1253 => "iso-8859-7", + 1254 => "iso-8859-9", + 50221 => "iso-2022-jp", + 50225 => "iso-2022-kr", + _ => WebName, + }; } } diff --git a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingTable.cs b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingTable.cs index 3b2a19cc80e0cc..d663e6273e283d 100644 --- a/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingTable.cs +++ b/src/libraries/System.Text.Encoding.CodePages/src/System/Text/EncodingTable.cs @@ -188,7 +188,7 @@ private static int CompareOrdinal(string s1, string s2, int index, int length) } } - //Nope, we didn't find it. + // Nope, we didn't find it. return null; } } diff --git a/src/libraries/System.Text.Encoding.CodePages/tests/EncodingCodePages.netcoreapp.cs b/src/libraries/System.Text.Encoding.CodePages/tests/EncodingCodePages.netcoreapp.cs new file mode 100644 index 00000000000000..34b899b8734c3d --- /dev/null +++ b/src/libraries/System.Text.Encoding.CodePages/tests/EncodingCodePages.netcoreapp.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.DotNet.RemoteExecutor; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Xunit; + +namespace System.Text.Tests +{ + public partial class EncodingTest : IClassFixture + { + private class EncodingInformation + { + public EncodingInformation(int codePage, string name) + { + CodePage = codePage; + Name = name; + } + + public int CodePage { get; } + public string Name { get; } + } + + private static EncodingInformation [] s_defaultEncoding = new EncodingInformation [] + { + new EncodingInformation(1200, "utf-16"), + new EncodingInformation(1201, "utf-16BE"), + new EncodingInformation(12000, "utf-32"), + new EncodingInformation(12001, "utf-32BE"), + new EncodingInformation(20127, "us-ascii"), + new EncodingInformation(28591, "iso-8859-1"), + new EncodingInformation(65001, "utf-8") + }; + + [Fact] + public void TestGetEncodings() + { + RemoteExecutor.Invoke(() => { + EncodingInfo [] list = Encoding.GetEncodings(); + + foreach (EncodingInformation eInfo in s_defaultEncoding) + { + Assert.NotNull(list.FirstOrDefault(o => o.CodePage == eInfo.CodePage && o.Name == eInfo.Name)); + } + }).Dispose(); + } + + [Fact] + public void TestGetEncodingsWithProvider() + { + RemoteExecutor.Invoke(() => { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + foreach (EncodingInfo ei in Encoding.GetEncodings()) + { + Encoding encoding = ei.GetEncoding(); + Assert.Equal(ei.CodePage, encoding.CodePage); + + Assert.True(ei.Name.Equals(encoding.WebName, StringComparison.OrdinalIgnoreCase), $"Encodinginfo.Name `{ei.Name}` != Encoding.WebName `{encoding.WebName}`"); + } + }).Dispose(); + } + } +} diff --git a/src/libraries/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj b/src/libraries/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj index b02a5646a7f8b3..353fe29ea301c2 100644 --- a/src/libraries/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj +++ b/src/libraries/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj @@ -6,5 +6,6 @@ + \ No newline at end of file