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.
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Globalization;
10 using System.Runtime.CompilerServices;
11 using System.Runtime.InteropServices;
12 using System.Runtime.Versioning;
13 using System.Security;
14 using System.Threading;
19 // Data table for encoding classes. Used by System.Text.Encoding.
20 // This class contains two hashtables to allow System.Text.Encoding
21 // to retrieve the data item either by codepage value or by webName.
24 // Only statics, does not need to be marked with the serializable attribute
25 internal static class EncodingTable
27 //This number is the size of the table in native. The value is retrieved by
28 //calling the native GetNumEncodingItems().
29 private static int lastEncodingItem = GetNumEncodingItems() - 1;
31 //This number is the size of the code page table. Its generated when we walk the table the first time.
32 private static volatile int lastCodePageItem;
35 // This points to a native data table which maps an encoding name to the correct code page.
37 internal static unsafe InternalEncodingDataItem* encodingDataPtr = GetEncodingData();
39 // This points to a native data table which stores the properties for the code page, and
40 // the table is indexed by code page.
42 internal static unsafe InternalCodePageDataItem* codePageDataPtr = GetCodePageData();
44 // This caches the mapping of an encoding name to a code page.
46 private static Hashtable hashByName = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
48 // THe caches the data item which is indexed by the code page value.
50 private static Hashtable hashByCodePage = Hashtable.Synchronized(new Hashtable());
52 // Find the data item by binary searching the table that we have in native.
53 // nativeCompareOrdinalWC is an internal-only function.
54 private static unsafe int internalGetCodePageFromName(String name)
57 int right = lastEncodingItem;
61 //Binary search the array until we have only a couple of elements left and then
62 //just walk those elements.
63 while ((right - left) > 3)
65 index = ((right - left) / 2) + left;
67 result = nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[index].webName);
71 //We found the item, return the associated codepage.
72 return (encodingDataPtr[index].codePage);
76 //The name that we're looking for is less than our current index.
81 //The name that we're looking for is greater than our current index
86 //Walk the remaining elements (it'll be 3 or fewer).
87 for (; left <= right; left++)
89 if (nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[left].webName) == 0)
91 return (encodingDataPtr[left].codePage);
94 // The encoding name is not valid.
95 throw new ArgumentException(
97 CultureInfo.CurrentCulture,
98 SR.Argument_EncodingNotSupported, name), nameof(name));
101 // Return a list of all EncodingInfo objects describing all of our encodings
102 internal static unsafe EncodingInfo[] GetEncodings()
104 if (lastCodePageItem == 0)
107 for (count = 0; codePageDataPtr[count].codePage != 0; count++)
111 lastCodePageItem = count;
114 EncodingInfo[] arrayEncodingInfo = new EncodingInfo[lastCodePageItem];
117 for (i = 0; i < lastCodePageItem; i++)
119 arrayEncodingInfo[i] = new EncodingInfo(codePageDataPtr[i].codePage, CodePageDataItem.CreateString(codePageDataPtr[i].Names, 0),
120 SR.GetResourceString("Globalization_cp_" + codePageDataPtr[i].codePage));
123 return arrayEncodingInfo;
126 /*=================================GetCodePageFromName==========================
127 **Action: Given a encoding name, return the correct code page number for this encoding.
128 **Returns: The code page for the encoding.
130 ** name the name of the encoding
132 ** ArgumentNullException if name is null.
133 ** internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name.
134 ============================================================================*/
136 internal static int GetCodePageFromName(String name)
140 throw new ArgumentNullException(nameof(name));
146 // The name is case-insensitive, but ToLower isn't free. Check for
147 // the code page in the given capitalization first.
149 codePageObj = hashByName[name];
151 if (codePageObj != null)
153 return ((int)codePageObj);
156 //Okay, we didn't find it in the hash table, try looking it up in the
158 int codePage = internalGetCodePageFromName(name);
160 hashByName[name] = codePage;
165 internal static unsafe CodePageDataItem GetCodePageDataItem(int codepage)
167 CodePageDataItem dataItem;
169 // We synchronize around dictionary gets/sets. There's still a possibility that two threads
170 // will create a CodePageDataItem and the second will clobber the first in the dictionary.
171 // However, that's acceptable because the contents are correct and we make no guarantees
174 //Look up the item in the hashtable.
175 dataItem = (CodePageDataItem)hashByCodePage[codepage];
177 //If we found it, return it.
178 if (dataItem != null)
184 //If we didn't find it, try looking it up now.
185 //If we find it, add it to the hashtable.
186 //This is a linear search, but we probably won't be doing it very often.
190 while ((data = codePageDataPtr[i].codePage) != 0)
192 if (data == codepage)
194 dataItem = new CodePageDataItem(i);
195 hashByCodePage[codepage] = dataItem;
201 //Nope, we didn't find it.
205 [MethodImplAttribute(MethodImplOptions.InternalCall)]
206 private static extern unsafe InternalEncodingDataItem* GetEncodingData();
209 // Return the number of encoding data items.
211 [MethodImplAttribute(MethodImplOptions.InternalCall)]
212 private static extern int GetNumEncodingItems();
214 [MethodImplAttribute(MethodImplOptions.InternalCall)]
215 private static extern unsafe InternalCodePageDataItem* GetCodePageData();
217 //This will not work in case-insensitive mode for any character greater than 0x7F.
218 //We'll throw an ArgumentException.
219 [MethodImplAttribute(MethodImplOptions.InternalCall)]
220 private static extern unsafe int nativeCompareOrdinalIgnoreCaseWC(String strA, sbyte* strBBytes);
223 /*=================================InternalEncodingDataItem==========================
224 **Action: This is used to map a encoding name to a correct code page number. By doing this,
225 ** we can get the properties of this encoding via the InternalCodePageDataItem.
227 ** We use this structure to access native data exposed by the native side.
228 ============================================================================*/
230 [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
231 internal unsafe struct InternalEncodingDataItem
233 internal sbyte* webName;
234 internal UInt16 codePage;
237 /*=================================InternalCodePageDataItem==========================
238 **Action: This is used to access the properties related to a code page.
239 ** We use this structure to access native data exposed by the native side.
240 ============================================================================*/
242 [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
243 internal unsafe struct InternalCodePageDataItem
245 internal UInt16 codePage;
246 internal UInt16 uiFamilyCodePage;
248 internal sbyte* Names;