EncodingTable.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. using System.Collections;
  5. using System.Diagnostics;
  6. using System.Threading;
  7. namespace System.Text
  8. {
  9. //
  10. // Data table for encoding classes. Used by System.Text.Encoding.
  11. // This class contains two hashtables to allow System.Text.Encoding
  12. // to retrieve the data item either by codepage value or by webName.
  13. //
  14. internal static partial class EncodingTable
  15. {
  16. private static readonly Hashtable s_nameToCodePage = Hashtable.Synchronized(new Hashtable(StringComparer.OrdinalIgnoreCase));
  17. private static CodePageDataItem[] s_codePageToCodePageData;
  18. /*=================================GetCodePageFromName==========================
  19. **Action: Given a encoding name, return the correct code page number for this encoding.
  20. **Returns: The code page for the encoding.
  21. **Arguments:
  22. ** name the name of the encoding
  23. **Exceptions:
  24. ** ArgumentNullException if name is null.
  25. ** internalGetCodePageFromName will throw ArgumentException if name is not a valid encoding name.
  26. ============================================================================*/
  27. internal static int GetCodePageFromName(string name)
  28. {
  29. if (name == null)
  30. throw new ArgumentNullException(nameof(name));
  31. object codePageObj;
  32. codePageObj = s_nameToCodePage[name];
  33. if (codePageObj != null)
  34. {
  35. return (int)codePageObj;
  36. }
  37. int codePage = InternalGetCodePageFromName(name);
  38. s_nameToCodePage[name] = codePage;
  39. return codePage;
  40. }
  41. // Find the data item by binary searching the table.
  42. private static int InternalGetCodePageFromName(string name)
  43. {
  44. int left = 0;
  45. int right = s_encodingNameIndices.Length - 2;
  46. int index;
  47. int result;
  48. Debug.Assert(s_encodingNameIndices.Length == s_codePagesByName.Length + 1);
  49. Debug.Assert(s_encodingNameIndices[s_encodingNameIndices.Length - 1] == s_encodingNames.Length);
  50. ReadOnlySpan<char> invariantName = name.ToLowerInvariant().AsSpan();
  51. //Binary search the array until we have only a couple of elements left and then
  52. //just walk those elements.
  53. while ((right - left) > 3)
  54. {
  55. index = ((right - left) / 2) + left;
  56. Debug.Assert(index < s_encodingNameIndices.Length - 1);
  57. result = string.CompareOrdinal(invariantName, s_encodingNames.AsSpan(s_encodingNameIndices[index], s_encodingNameIndices[index + 1] - s_encodingNameIndices[index]));
  58. if (result == 0)
  59. {
  60. //We found the item, return the associated codePage.
  61. return s_codePagesByName[index];
  62. }
  63. else if (result < 0)
  64. {
  65. //The name that we're looking for is less than our current index.
  66. right = index;
  67. }
  68. else
  69. {
  70. //The name that we're looking for is greater than our current index
  71. left = index;
  72. }
  73. }
  74. //Walk the remaining elements (it'll be 3 or fewer).
  75. for (; left <= right; left++)
  76. {
  77. Debug.Assert(left < s_encodingNameIndices.Length - 1);
  78. if (string.CompareOrdinal(invariantName, s_encodingNames.AsSpan(s_encodingNameIndices[left], s_encodingNameIndices[left + 1] - s_encodingNameIndices[left])) == 0)
  79. {
  80. return s_codePagesByName[left];
  81. }
  82. }
  83. // The encoding name is not valid.
  84. throw new ArgumentException(
  85. SR.Format(SR.Argument_EncodingNotSupported, name),
  86. nameof(name));
  87. }
  88. // Return a list of all EncodingInfo objects describing all of our encodings
  89. internal static EncodingInfo[] GetEncodings()
  90. {
  91. EncodingInfo[] arrayEncodingInfo = new EncodingInfo[s_mappedCodePages.Length];
  92. for (int i = 0; i < s_mappedCodePages.Length; i++)
  93. {
  94. arrayEncodingInfo[i] = new EncodingInfo(
  95. s_mappedCodePages[i],
  96. s_webNames.Substring(s_webNameIndices[i], s_webNameIndices[i + 1] - s_webNameIndices[i]),
  97. GetDisplayName(s_mappedCodePages[i], i)
  98. );
  99. }
  100. return arrayEncodingInfo;
  101. }
  102. internal static CodePageDataItem GetCodePageDataItem(int codePage)
  103. {
  104. if (s_codePageToCodePageData == null)
  105. {
  106. Interlocked.CompareExchange(ref s_codePageToCodePageData, new CodePageDataItem[s_mappedCodePages.Length], null);
  107. }
  108. // Keep in sync with s_mappedCodePages
  109. int index;
  110. switch (codePage)
  111. {
  112. case 1200: // utf-16
  113. index = 0;
  114. break;
  115. case 1201: // utf-16be
  116. index = 1;
  117. break;
  118. case 12000: // utf-32
  119. index = 2;
  120. break;
  121. case 12001: // utf-32be
  122. index = 3;
  123. break;
  124. case 20127: // us-ascii
  125. index = 4;
  126. break;
  127. case 28591: // iso-8859-1
  128. index = 5;
  129. break;
  130. case 65000: // utf-7
  131. index = 6;
  132. break;
  133. case 65001: // utf-8
  134. index = 7;
  135. break;
  136. default:
  137. return null;
  138. }
  139. CodePageDataItem data = s_codePageToCodePageData[index];
  140. if (data == null)
  141. {
  142. Interlocked.CompareExchange(ref s_codePageToCodePageData[index], InternalGetCodePageDataItem(codePage, index), null);
  143. data = s_codePageToCodePageData[index];
  144. }
  145. return data;
  146. }
  147. private static CodePageDataItem InternalGetCodePageDataItem(int codePage, int index)
  148. {
  149. int uiFamilyCodePage = s_uiFamilyCodePages[index];
  150. string webName = s_webNames.Substring(s_webNameIndices[index], s_webNameIndices[index + 1] - s_webNameIndices[index]);
  151. // All supported code pages have identical header names, and body names.
  152. string headerName = webName;
  153. string bodyName = webName;
  154. string displayName = GetDisplayName(codePage, index);
  155. uint flags = s_flags[index];
  156. return new CodePageDataItem(codePage, uiFamilyCodePage, webName, headerName, bodyName, displayName, flags);
  157. }
  158. private static string GetDisplayName(int codePage, int englishNameIndex)
  159. {
  160. string displayName = SR.GetResourceString("Globalization_cp_" + codePage.ToString());
  161. if (string.IsNullOrEmpty(displayName))
  162. displayName = s_englishNames.Substring(s_englishNameIndices[englishNameIndex], s_englishNameIndices[englishNameIndex + 1] - s_englishNameIndices[englishNameIndex]);
  163. return displayName;
  164. }
  165. }
  166. }