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.Collections.Generic; 7 using System.Diagnostics; 8 using System.Threading; 9 10 namespace System.Text 11 { 12 internal static partial class EncodingTable 13 { 14 private static readonly Dictionary<string, int> s_nameToCodePageCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); 15 private static readonly Dictionary<int, string> s_codePageToWebNameCache = new Dictionary<int, string>(); 16 private static readonly Dictionary<int, string> s_codePageToEnglishNameCache = new Dictionary<int, string>(); 17 private static readonly ReaderWriterLockSlim s_cacheLock = new ReaderWriterLockSlim(); 18 GetCodePageFromName(string name)19 internal static int GetCodePageFromName(string name) 20 { 21 if (name == null) 22 return 0; 23 24 int codePage; 25 26 s_cacheLock.EnterUpgradeableReadLock(); 27 try 28 { 29 if (s_nameToCodePageCache.TryGetValue(name, out codePage)) 30 { 31 return codePage; 32 } 33 else 34 { 35 // Okay, we didn't find it in the hash table, try looking it up in the unmanaged data. 36 codePage = InternalGetCodePageFromName(name); 37 if (codePage == 0) 38 return 0; 39 40 s_cacheLock.EnterWriteLock(); 41 try 42 { 43 int cachedCodePage; 44 if (s_nameToCodePageCache.TryGetValue(name, out cachedCodePage)) 45 { 46 return cachedCodePage; 47 } 48 s_nameToCodePageCache.Add(name, codePage); 49 } 50 finally 51 { 52 s_cacheLock.ExitWriteLock(); 53 } 54 } 55 } 56 finally 57 { 58 s_cacheLock.ExitUpgradeableReadLock(); 59 } 60 61 return codePage; 62 } 63 InternalGetCodePageFromName(string name)64 private static int InternalGetCodePageFromName(string name) 65 { 66 int left = 0; 67 int right = s_encodingNameIndices.Length - 2; 68 int index; 69 int result; 70 71 Debug.Assert(s_encodingNameIndices.Length == s_codePagesByName.Length + 1); 72 Debug.Assert(s_encodingNameIndices[s_encodingNameIndices.Length - 1] == s_encodingNames.Length); 73 74 name = name.ToLowerInvariant(); 75 76 //Binary search the array until we have only a couple of elements left and then 77 //just walk those elements. 78 while ((right - left) > 3) 79 { 80 index = ((right - left) / 2) + left; 81 82 Debug.Assert(index < s_encodingNameIndices.Length - 1); 83 result = CompareOrdinal(name, s_encodingNames, s_encodingNameIndices[index], s_encodingNameIndices[index + 1] - s_encodingNameIndices[index]); 84 if (result == 0) 85 { 86 //We found the item, return the associated codePage. 87 return (s_codePagesByName[index]); 88 } 89 else if (result < 0) 90 { 91 //The name that we're looking for is less than our current index. 92 right = index; 93 } 94 else 95 { 96 //The name that we're looking for is greater than our current index 97 left = index; 98 } 99 } 100 101 //Walk the remaining elements (it'll be 3 or fewer). 102 for (; left <= right; left++) 103 { 104 Debug.Assert(left < s_encodingNameIndices.Length - 1); 105 if (CompareOrdinal(name, s_encodingNames, s_encodingNameIndices[left], s_encodingNameIndices[left + 1] - s_encodingNameIndices[left]) == 0) 106 { 107 return (s_codePagesByName[left]); 108 } 109 } 110 111 // The encoding name is not valid. 112 return 0; 113 } 114 CompareOrdinal(string s1, string s2, int index, int length)115 private static int CompareOrdinal(string s1, string s2, int index, int length) 116 { 117 int count = s1.Length; 118 if (count > length) 119 count = length; 120 121 int i = 0; 122 while (i < count && s1[i] == s2[index + i]) 123 i++; 124 125 if (i < count) 126 return (int)(s1[i] - s2[index + i]); 127 128 return s1.Length - length; 129 } 130 GetWebNameFromCodePage(int codePage)131 internal static string GetWebNameFromCodePage(int codePage) 132 { 133 return GetNameFromCodePage(codePage, s_webNames, s_webNameIndices, s_codePageToWebNameCache); 134 } 135 GetEnglishNameFromCodePage(int codePage)136 internal static string GetEnglishNameFromCodePage(int codePage) 137 { 138 return GetNameFromCodePage(codePage, s_englishNames, s_englishNameIndices, s_codePageToEnglishNameCache); 139 } 140 GetNameFromCodePage(int codePage, string names, int[] indices, Dictionary<int, string> cache)141 private static string GetNameFromCodePage(int codePage, string names, int[] indices, Dictionary<int, string> cache) 142 { 143 string name; 144 145 Debug.Assert(s_mappedCodePages.Length + 1 == indices.Length); 146 Debug.Assert(indices[indices.Length - 1] == names.Length); 147 148 //This is a linear search, but we probably won't be doing it very often. 149 for (int i = 0; i < s_mappedCodePages.Length; i++) 150 { 151 if (s_mappedCodePages[i] == codePage) 152 { 153 Debug.Assert(i < indices.Length - 1); 154 155 s_cacheLock.EnterUpgradeableReadLock(); 156 try 157 { 158 if (cache.TryGetValue(codePage, out name)) 159 { 160 return name; 161 } 162 else 163 { 164 name = names.Substring(indices[i], indices[i + 1] - indices[i]); 165 166 s_cacheLock.EnterWriteLock(); 167 try 168 { 169 string cachedName; 170 if (cache.TryGetValue(codePage, out cachedName)) 171 { 172 return cachedName; 173 } 174 175 cache.Add(codePage, name); 176 } 177 finally 178 { 179 s_cacheLock.ExitWriteLock(); 180 } 181 } 182 } 183 finally 184 { 185 s_cacheLock.ExitUpgradeableReadLock(); 186 } 187 188 return name; 189 } 190 } 191 192 //Nope, we didn't find it. 193 return null; 194 } 195 } 196 } 197