| | | 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 | | |
| | | 4 | | using System.Collections.Concurrent; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Diagnostics; |
| | | 7 | | using System.Threading; |
| | | 8 | | |
| | | 9 | | namespace System.Text |
| | | 10 | | { |
| | | 11 | | // |
| | | 12 | | // Data table for encoding classes. Used by System.Text.Encoding. |
| | | 13 | | // This class contains two hashtables to allow System.Text.Encoding |
| | | 14 | | // to retrieve the data item either by codepage value or by webName. |
| | | 15 | | // |
| | | 16 | | internal static partial class EncodingTable |
| | | 17 | | { |
| | | 18 | | private static ConcurrentDictionary<string, int>? s_nameToCodePage; |
| | | 19 | | private static CodePageDataItem?[]? s_codePageToCodePageData; |
| | | 20 | | |
| | | 21 | | /*=================================GetCodePageFromName========================== |
| | | 22 | | **Action: Given a encoding name, return the correct code page number for this encoding. |
| | | 23 | | **Returns: The code page for the encoding. |
| | | 24 | | **Arguments: |
| | | 25 | | ** name the name of the encoding |
| | | 26 | | **Exceptions: |
| | | 27 | | ** ArgumentNullException if name is null. |
| | | 28 | | ** internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name. |
| | | 29 | | ============================================================================*/ |
| | | 30 | | |
| | | 31 | | internal static int GetCodePageFromName(string name) |
| | | 32 | | { |
| | 6806 | 33 | | ArgumentNullException.ThrowIfNull(name); |
| | | 34 | | |
| | 6806 | 35 | | if (s_nameToCodePage == null) |
| | | 36 | | { |
| | 1 | 37 | | Interlocked.CompareExchange(ref s_nameToCodePage, new(StringComparer.OrdinalIgnoreCase), null); |
| | | 38 | | } |
| | | 39 | | |
| | 6806 | 40 | | return s_nameToCodePage.GetOrAdd(name, InternalGetCodePageFromName); |
| | | 41 | | } |
| | | 42 | | |
| | | 43 | | // Find the data item by binary searching the table. |
| | | 44 | | private static int InternalGetCodePageFromName(string name) |
| | | 45 | | { |
| | 1 | 46 | | int left = 0; |
| | 1 | 47 | | int right = EncodingNameIndices.Length - 2; |
| | | 48 | | int index; |
| | | 49 | | int result; |
| | | 50 | | |
| | 1 | 51 | | Debug.Assert(EncodingNameIndices.Length == CodePagesByName.Length + 1); |
| | 1 | 52 | | Debug.Assert(EncodingNameIndices[^1] == EncodingNames.Length); |
| | | 53 | | |
| | 1 | 54 | | ReadOnlySpan<char> invariantName = name.ToLowerInvariant().AsSpan(); |
| | | 55 | | |
| | | 56 | | // Binary search the array until we have only a couple of elements left and then |
| | | 57 | | // just walk those elements. |
| | 2 | 58 | | while ((right - left) > 3) |
| | | 59 | | { |
| | 2 | 60 | | index = ((right - left) / 2) + left; |
| | | 61 | | |
| | 2 | 62 | | Debug.Assert(index < EncodingNameIndices.Length - 1); |
| | 2 | 63 | | result = string.CompareOrdinal(invariantName, EncodingNames.AsSpan(EncodingNameIndices[index], EncodingN |
| | | 64 | | |
| | 2 | 65 | | if (result == 0) |
| | | 66 | | { |
| | | 67 | | // We found the item, return the associated codePage. |
| | 1 | 68 | | return CodePagesByName[index]; |
| | | 69 | | } |
| | 1 | 70 | | else if (result < 0) |
| | | 71 | | { |
| | | 72 | | // The name that we're looking for is less than our current index. |
| | 1 | 73 | | right = index; |
| | | 74 | | } |
| | | 75 | | else |
| | | 76 | | { |
| | | 77 | | // The name that we're looking for is greater than our current index |
| | 0 | 78 | | left = index; |
| | | 79 | | } |
| | | 80 | | } |
| | | 81 | | |
| | | 82 | | // Walk the remaining elements (it'll be 3 or fewer). |
| | 0 | 83 | | for (; left <= right; left++) |
| | | 84 | | { |
| | 0 | 85 | | Debug.Assert(left < EncodingNameIndices.Length - 1); |
| | 0 | 86 | | if (invariantName.SequenceEqual(EncodingNames.AsSpan(EncodingNameIndices[left], EncodingNameIndices[left |
| | | 87 | | { |
| | 0 | 88 | | return CodePagesByName[left]; |
| | | 89 | | } |
| | | 90 | | } |
| | | 91 | | |
| | | 92 | | // The encoding name is not valid. |
| | 0 | 93 | | throw new ArgumentException( |
| | 0 | 94 | | SR.Format(SR.Argument_EncodingNotSupported, name), |
| | 0 | 95 | | nameof(name)); |
| | | 96 | | } |
| | | 97 | | |
| | | 98 | | // Return a list of all EncodingInfo objects describing all of our encodings |
| | | 99 | | internal static EncodingInfo[] GetEncodings() |
| | | 100 | | { |
| | | 101 | | // If UTF-7 encoding is not enabled, we adjust the return array length by -1 |
| | | 102 | | // to account for the skipped EncodingInfo element. |
| | | 103 | | |
| | 0 | 104 | | ReadOnlySpan<ushort> mappedCodePages = MappedCodePages; |
| | 0 | 105 | | EncodingInfo[] arrayEncodingInfo = new EncodingInfo[(LocalAppContextSwitches.EnableUnsafeUTF7Encoding) ? map |
| | 0 | 106 | | string webNames = WebNames; |
| | 0 | 107 | | ReadOnlySpan<int> webNameIndices = WebNameIndices; |
| | 0 | 108 | | int arrayEncodingInfoIdx = 0; |
| | | 109 | | |
| | 0 | 110 | | for (int i = 0; i < mappedCodePages.Length; i++) |
| | | 111 | | { |
| | 0 | 112 | | int codePage = mappedCodePages[i]; |
| | 0 | 113 | | if (codePage == Encoding.CodePageUTF7 && !LocalAppContextSwitches.EnableUnsafeUTF7Encoding) |
| | | 114 | | { |
| | | 115 | | continue; // skip this entry; UTF-7 is disabled |
| | | 116 | | } |
| | | 117 | | |
| | 0 | 118 | | arrayEncodingInfo[arrayEncodingInfoIdx++] = new EncodingInfo( |
| | 0 | 119 | | codePage, |
| | 0 | 120 | | webNames[webNameIndices[i]..webNameIndices[i + 1]], |
| | 0 | 121 | | GetDisplayName(codePage) |
| | 0 | 122 | | ); |
| | | 123 | | } |
| | | 124 | | |
| | 0 | 125 | | Debug.Assert(arrayEncodingInfoIdx == arrayEncodingInfo.Length); |
| | 0 | 126 | | return arrayEncodingInfo; |
| | | 127 | | } |
| | | 128 | | |
| | | 129 | | internal static EncodingInfo[] GetEncodings(Dictionary<int, EncodingInfo> encodingInfoList) |
| | | 130 | | { |
| | 0 | 131 | | Debug.Assert(encodingInfoList != null); |
| | 0 | 132 | | ReadOnlySpan<ushort> mappedCodePages = MappedCodePages; |
| | 0 | 133 | | string webNames = WebNames; |
| | 0 | 134 | | ReadOnlySpan<int> webNameIndices = WebNameIndices; |
| | | 135 | | |
| | 0 | 136 | | for (int i = 0; i < mappedCodePages.Length; i++) |
| | | 137 | | { |
| | 0 | 138 | | int codePage = mappedCodePages[i]; |
| | 0 | 139 | | if (!encodingInfoList.ContainsKey(codePage)) |
| | | 140 | | { |
| | | 141 | | // If UTF-7 encoding is not enabled, don't add it to the provided dictionary instance. |
| | | 142 | | // Exception: If somebody already registered a custom UTF-7 provider, the dictionary |
| | | 143 | | // will already contain an entry for the UTF-7 code page key, and we'll let it go through. |
| | | 144 | | |
| | 0 | 145 | | if (codePage != Encoding.CodePageUTF7 || LocalAppContextSwitches.EnableUnsafeUTF7Encoding) |
| | | 146 | | { |
| | 0 | 147 | | encodingInfoList[codePage] = new EncodingInfo(codePage, webNames[webNameIndices[i]..webNameIndic |
| | 0 | 148 | | GetDisplayName(codePage)); |
| | | 149 | | } |
| | | 150 | | } |
| | | 151 | | } |
| | | 152 | | |
| | | 153 | | // Just in case a provider registered UTF-7 without the application's consent |
| | | 154 | | |
| | 0 | 155 | | if (!LocalAppContextSwitches.EnableUnsafeUTF7Encoding) |
| | | 156 | | { |
| | 0 | 157 | | encodingInfoList.Remove(Encoding.CodePageUTF7); // won't throw if doesn't exist |
| | | 158 | | } |
| | | 159 | | |
| | 0 | 160 | | var result = new EncodingInfo[encodingInfoList.Count]; |
| | 0 | 161 | | int j = 0; |
| | 0 | 162 | | foreach (KeyValuePair<int, EncodingInfo> pair in encodingInfoList) |
| | | 163 | | { |
| | 0 | 164 | | result[j++] = pair.Value; |
| | | 165 | | } |
| | | 166 | | |
| | 0 | 167 | | return result; |
| | | 168 | | } |
| | | 169 | | |
| | | 170 | | internal static CodePageDataItem? GetCodePageDataItem(int codePage) |
| | | 171 | | { |
| | 0 | 172 | | if (s_codePageToCodePageData == null) |
| | | 173 | | { |
| | 0 | 174 | | Interlocked.CompareExchange<CodePageDataItem?[]?>(ref s_codePageToCodePageData, new CodePageDataItem[Map |
| | | 175 | | } |
| | | 176 | | |
| | | 177 | | // Keep in sync with MappedCodePages |
| | | 178 | | int index; |
| | | 179 | | switch (codePage) |
| | | 180 | | { |
| | | 181 | | case 1200: // utf-16 |
| | 0 | 182 | | index = 0; |
| | 0 | 183 | | break; |
| | | 184 | | case 1201: // utf-16be |
| | 0 | 185 | | index = 1; |
| | 0 | 186 | | break; |
| | | 187 | | case 12000: // utf-32 |
| | 0 | 188 | | index = 2; |
| | 0 | 189 | | break; |
| | | 190 | | case 12001: // utf-32be |
| | 0 | 191 | | index = 3; |
| | 0 | 192 | | break; |
| | | 193 | | case 20127: // us-ascii |
| | 0 | 194 | | index = 4; |
| | 0 | 195 | | break; |
| | | 196 | | case 28591: // iso-8859-1 |
| | 0 | 197 | | index = 5; |
| | 0 | 198 | | break; |
| | | 199 | | case 65000: // utf-7 |
| | 0 | 200 | | index = 6; |
| | 0 | 201 | | break; |
| | | 202 | | case 65001: // utf-8 |
| | 0 | 203 | | index = 7; |
| | 0 | 204 | | break; |
| | | 205 | | default: |
| | 0 | 206 | | return null; |
| | | 207 | | } |
| | | 208 | | |
| | 0 | 209 | | CodePageDataItem? data = s_codePageToCodePageData[index]; |
| | 0 | 210 | | if (data == null) |
| | | 211 | | { |
| | 0 | 212 | | Interlocked.CompareExchange<CodePageDataItem?>(ref s_codePageToCodePageData[index], InternalGetCodePageD |
| | 0 | 213 | | data = s_codePageToCodePageData[index]; |
| | | 214 | | } |
| | | 215 | | |
| | 0 | 216 | | return data; |
| | | 217 | | } |
| | | 218 | | |
| | | 219 | | private static CodePageDataItem InternalGetCodePageDataItem(int codePage, int index) |
| | | 220 | | { |
| | 0 | 221 | | int uiFamilyCodePage = UiFamilyCodePages[index]; |
| | 0 | 222 | | string webName = WebNames[WebNameIndices[index]..WebNameIndices[index + 1]]; |
| | | 223 | | // All supported code pages have identical header names, and body names. |
| | 0 | 224 | | string headerName = webName; |
| | 0 | 225 | | string bodyName = webName; |
| | 0 | 226 | | string displayName = GetDisplayName(codePage); |
| | 0 | 227 | | uint flags = Flags[index]; |
| | | 228 | | |
| | 0 | 229 | | return new CodePageDataItem(uiFamilyCodePage, webName, headerName, bodyName, displayName, flags); |
| | | 230 | | } |
| | | 231 | | |
| | | 232 | | private static string GetDisplayName(int codePage) |
| | | 233 | | { |
| | | 234 | | switch (codePage) |
| | | 235 | | { |
| | 0 | 236 | | case 1200: return SR.Globalization_cp_1200; |
| | 0 | 237 | | case 1201: return SR.Globalization_cp_1201; |
| | 0 | 238 | | case 12000: return SR.Globalization_cp_12000; |
| | 0 | 239 | | case 12001: return SR.Globalization_cp_12001; |
| | 0 | 240 | | case 20127: return SR.Globalization_cp_20127; |
| | 0 | 241 | | case 28591: return SR.Globalization_cp_28591; |
| | 0 | 242 | | case 65000: return SR.Globalization_cp_65000; |
| | 0 | 243 | | case 65001: return SR.Globalization_cp_65001; |
| | | 244 | | }; |
| | | 245 | | |
| | 0 | 246 | | Debug.Fail("Unexpected code page"); |
| | | 247 | | return ""; |
| | | 248 | | } |
| | | 249 | | } |
| | | 250 | | } |
| | | 251 | | |