Fix copy&paste bug (#17059)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Text / EncodingTable.cs
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;
6 using System.Text;
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;
15
16 namespace System.Text
17 {
18     //
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.
22     //
23
24     // Only statics, does not need to be marked with the serializable attribute
25     internal static class EncodingTable
26     {
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;
30
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;
33
34         //
35         // This points to a native data table which maps an encoding name to the correct code page.        
36         //
37         internal static unsafe InternalEncodingDataItem* encodingDataPtr = GetEncodingData();
38         //
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.
41         //
42         internal static unsafe InternalCodePageDataItem* codePageDataPtr = GetCodePageData();
43         //
44         // This caches the mapping of an encoding name to a code page.
45         //
46         private static Hashtable hashByName = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
47         //
48         // THe caches the data item which is indexed by the code page value.
49         //
50         private static Hashtable hashByCodePage = Hashtable.Synchronized(new Hashtable());
51
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)
55         {
56             int left = 0;
57             int right = lastEncodingItem;
58             int index;
59             int result;
60
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)
64             {
65                 index = ((right - left) / 2) + left;
66
67                 result = nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[index].webName);
68
69                 if (result == 0)
70                 {
71                     //We found the item, return the associated codepage.
72                     return (encodingDataPtr[index].codePage);
73                 }
74                 else if (result < 0)
75                 {
76                     //The name that we're looking for is less than our current index.
77                     right = index;
78                 }
79                 else
80                 {
81                     //The name that we're looking for is greater than our current index
82                     left = index;
83                 }
84             }
85
86             //Walk the remaining elements (it'll be 3 or fewer).
87             for (; left <= right; left++)
88             {
89                 if (nativeCompareOrdinalIgnoreCaseWC(name, encodingDataPtr[left].webName) == 0)
90                 {
91                     return (encodingDataPtr[left].codePage);
92                 }
93             }
94             // The encoding name is not valid.
95             throw new ArgumentException(
96                 String.Format(
97                     CultureInfo.CurrentCulture,
98                     SR.Argument_EncodingNotSupported, name), nameof(name));
99         }
100
101         // Return a list of all EncodingInfo objects describing all of our encodings
102         internal static unsafe EncodingInfo[] GetEncodings()
103         {
104             if (lastCodePageItem == 0)
105             {
106                 int count;
107                 for (count = 0; codePageDataPtr[count].codePage != 0; count++)
108                 {
109                     // Count them
110                 }
111                 lastCodePageItem = count;
112             }
113
114             EncodingInfo[] arrayEncodingInfo = new EncodingInfo[lastCodePageItem];
115
116             int i;
117             for (i = 0; i < lastCodePageItem; i++)
118             {
119                 arrayEncodingInfo[i] = new EncodingInfo(codePageDataPtr[i].codePage, CodePageDataItem.CreateString(codePageDataPtr[i].Names, 0),
120                     SR.GetResourceString("Globalization_cp_" + codePageDataPtr[i].codePage));
121             }
122
123             return arrayEncodingInfo;
124         }
125
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.
129         **Arguments:
130         **  name    the name of the encoding
131         **Exceptions:
132         **  ArgumentNullException if name is null.
133         **  internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name.
134         ============================================================================*/
135
136         internal static int GetCodePageFromName(String name)
137         {
138             if (name == null)
139             {
140                 throw new ArgumentNullException(nameof(name));
141             }
142
143             Object codePageObj;
144
145             //
146             // The name is case-insensitive, but ToLower isn't free.  Check for
147             // the code page in the given capitalization first.
148             //
149             codePageObj = hashByName[name];
150
151             if (codePageObj != null)
152             {
153                 return ((int)codePageObj);
154             }
155
156             //Okay, we didn't find it in the hash table, try looking it up in the 
157             //unmanaged data.
158             int codePage = internalGetCodePageFromName(name);
159
160             hashByName[name] = codePage;
161
162             return codePage;
163         }
164
165         internal static unsafe CodePageDataItem GetCodePageDataItem(int codepage)
166         {
167             CodePageDataItem dataItem;
168
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
172             // other than that. 
173
174             //Look up the item in the hashtable.
175             dataItem = (CodePageDataItem)hashByCodePage[codepage];
176
177             //If we found it, return it.
178             if (dataItem != null)
179             {
180                 return dataItem;
181             }
182
183
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.
187             //
188             int i = 0;
189             int data;
190             while ((data = codePageDataPtr[i].codePage) != 0)
191             {
192                 if (data == codepage)
193                 {
194                     dataItem = new CodePageDataItem(i);
195                     hashByCodePage[codepage] = dataItem;
196                     return (dataItem);
197                 }
198                 i++;
199             }
200
201             //Nope, we didn't find it.
202             return null;
203         }
204
205         [MethodImplAttribute(MethodImplOptions.InternalCall)]
206         private static extern unsafe InternalEncodingDataItem* GetEncodingData();
207
208         //
209         // Return the number of encoding data items.
210         //
211         [MethodImplAttribute(MethodImplOptions.InternalCall)]
212         private static extern int GetNumEncodingItems();
213
214         [MethodImplAttribute(MethodImplOptions.InternalCall)]
215         private static extern unsafe InternalCodePageDataItem* GetCodePageData();
216
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);
221     }
222
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.
226     **
227     ** We use this structure to access native data exposed by the native side.
228     ============================================================================*/
229
230     [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
231     internal unsafe struct InternalEncodingDataItem
232     {
233         internal sbyte* webName;
234         internal UInt16 codePage;
235     }
236
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     ============================================================================*/
241
242     [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
243     internal unsafe struct InternalCodePageDataItem
244     {
245         internal UInt16 codePage;
246         internal UInt16 uiFamilyCodePage;
247         internal uint flags;
248         internal sbyte* Names;
249     }
250 }