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.Runtime.CompilerServices;
10 using System.Runtime.InteropServices;
11 using System.Runtime.Versioning;
12 using System.Security;
13 using System.Threading;
15 namespace System.Globalization
18 // Data table for encoding classes. Used by System.Text.Encoding.
19 // This class contains two hashtables to allow System.Text.Encoding
20 // to retrieve the data item either by codepage value or by webName.
23 // Only statics, does not need to be marked with the serializable attribute
24 internal static class EncodingTable
26 //This number is the size of the table in native. The value is retrieved by
27 //calling the native GetNumEncodingItems().
28 private static int lastEncodingItem = GetNumEncodingItems() - 1;
30 //This number is the size of the code page table. Its generated when we walk the table the first time.
31 private static volatile int lastCodePageItem;
34 // This points to a native data table which maps an encoding name to the correct code page.
36 internal static unsafe InternalEncodingDataItem* encodingDataPtr = GetEncodingData();
38 // This points to a native data table which stores the properties for the code page, and
39 // the table is indexed by code page.
41 internal static unsafe InternalCodePageDataItem* codePageDataPtr = GetCodePageData();
43 // This caches the mapping of an encoding name to a code page.
45 private static Hashtable hashByName = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
47 // THe caches the data item which is indexed by the code page value.
49 private static Hashtable hashByCodePage = Hashtable.Synchronized(new Hashtable());
51 // Find the data item by binary searching the table that we have in native.
52 // nativeCompareOrdinalWC is an internal-only function.
53 private static unsafe int internalGetCodePageFromName(String name)
56 int right = lastEncodingItem;
60 //Binary search the array until we have only a couple of elements left and then
61 //just walk those elements.
62 while ((right - left) > 3)
64 index = ((right - left) / 2) + left;
66 result = String.nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[index].webName);
70 //We found the item, return the associated codepage.
71 return (encodingDataPtr[index].codePage);
75 //The name that we're looking for is less than our current index.
80 //The name that we're looking for is greater than our current index
85 //Walk the remaining elements (it'll be 3 or fewer).
86 for (; left <= right; left++)
88 if (String.nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[left].webName) == 0)
90 return (encodingDataPtr[left].codePage);
93 // The encoding name is not valid.
94 throw new ArgumentException(
96 CultureInfo.CurrentCulture,
97 SR.Argument_EncodingNotSupported, name), nameof(name));
100 // Return a list of all EncodingInfo objects describing all of our encodings
101 internal static unsafe EncodingInfo[] GetEncodings()
103 if (lastCodePageItem == 0)
106 for (count = 0; codePageDataPtr[count].codePage != 0; count++)
110 lastCodePageItem = count;
113 EncodingInfo[] arrayEncodingInfo = new EncodingInfo[lastCodePageItem];
116 for (i = 0; i < lastCodePageItem; i++)
118 arrayEncodingInfo[i] = new EncodingInfo(codePageDataPtr[i].codePage, CodePageDataItem.CreateString(codePageDataPtr[i].Names, 0),
119 SR.GetResourceString("Globalization_cp_" + codePageDataPtr[i].codePage));
122 return arrayEncodingInfo;
125 /*=================================GetCodePageFromName==========================
126 **Action: Given a encoding name, return the correct code page number for this encoding.
127 **Returns: The code page for the encoding.
129 ** name the name of the encoding
131 ** ArgumentNullException if name is null.
132 ** internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name.
133 ============================================================================*/
135 internal static int GetCodePageFromName(String name)
139 throw new ArgumentNullException(nameof(name));
145 // The name is case-insensitive, but ToLower isn't free. Check for
146 // the code page in the given capitalization first.
148 codePageObj = hashByName[name];
150 if (codePageObj != null)
152 return ((int)codePageObj);
155 //Okay, we didn't find it in the hash table, try looking it up in the
157 int codePage = internalGetCodePageFromName(name);
159 hashByName[name] = codePage;
164 internal static unsafe CodePageDataItem GetCodePageDataItem(int codepage)
166 CodePageDataItem dataItem;
168 // We synchronize around dictionary gets/sets. There's still a possibility that two threads
169 // will create a CodePageDataItem and the second will clobber the first in the dictionary.
170 // However, that's acceptable because the contents are correct and we make no guarantees
173 //Look up the item in the hashtable.
174 dataItem = (CodePageDataItem)hashByCodePage[codepage];
176 //If we found it, return it.
177 if (dataItem != null)
183 //If we didn't find it, try looking it up now.
184 //If we find it, add it to the hashtable.
185 //This is a linear search, but we probably won't be doing it very often.
189 while ((data = codePageDataPtr[i].codePage) != 0)
191 if (data == codepage)
193 dataItem = new CodePageDataItem(i);
194 hashByCodePage[codepage] = dataItem;
200 //Nope, we didn't find it.
204 [MethodImplAttribute(MethodImplOptions.InternalCall)]
205 private static extern unsafe InternalEncodingDataItem* GetEncodingData();
208 // Return the number of encoding data items.
210 [MethodImplAttribute(MethodImplOptions.InternalCall)]
211 private static extern int GetNumEncodingItems();
213 [MethodImplAttribute(MethodImplOptions.InternalCall)]
214 private static extern unsafe InternalCodePageDataItem* GetCodePageData();
217 /*=================================InternalEncodingDataItem==========================
218 **Action: This is used to map a encoding name to a correct code page number. By doing this,
219 ** we can get the properties of this encoding via the InternalCodePageDataItem.
221 ** We use this structure to access native data exposed by the native side.
222 ============================================================================*/
224 [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
225 internal unsafe struct InternalEncodingDataItem
227 internal sbyte* webName;
228 internal UInt16 codePage;
231 /*=================================InternalCodePageDataItem==========================
232 **Action: This is used to access the properties related to a code page.
233 ** We use this structure to access native data exposed by the native side.
234 ============================================================================*/
236 [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
237 internal unsafe struct InternalCodePageDataItem
239 internal UInt16 codePage;
240 internal UInt16 uiFamilyCodePage;
242 internal sbyte* Names;