Merge pull request #12748 from helloguo/NUMASupportInitialize
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Text / EncoderBestFitFallback.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 //
6 // This is used internally to create best fit behavior as per the original windows best fit behavior.
7 //
8
9 using System.Diagnostics;
10 using System.Diagnostics.Contracts;
11 using System.Globalization;
12 using System.Threading;
13
14 namespace System.Text
15 {
16     internal class InternalEncoderBestFitFallback : EncoderFallback
17     {
18         // Our variables
19         internal Encoding _encoding = null;
20         internal char[] _arrayBestFit = null;
21
22         internal InternalEncoderBestFitFallback(Encoding encoding)
23         {
24             // Need to load our replacement characters table.
25             _encoding = encoding;
26         }
27
28         public override EncoderFallbackBuffer CreateFallbackBuffer()
29         {
30             return new InternalEncoderBestFitFallbackBuffer(this);
31         }
32
33         // Maximum number of characters that this instance of this fallback could return
34         public override int MaxCharCount
35         {
36             get
37             {
38                 return 1;
39             }
40         }
41
42         public override bool Equals(Object value)
43         {
44             InternalEncoderBestFitFallback that = value as InternalEncoderBestFitFallback;
45             if (that != null)
46             {
47                 return (_encoding.CodePage == that._encoding.CodePage);
48             }
49             return (false);
50         }
51
52         public override int GetHashCode()
53         {
54             return _encoding.CodePage;
55         }
56     }
57
58     internal sealed class InternalEncoderBestFitFallbackBuffer : EncoderFallbackBuffer
59     {
60         // Our variables
61         private char _cBestFit = '\0';
62         private InternalEncoderBestFitFallback _oFallback;
63         private int _iCount = -1;
64         private int _iSize;
65
66         // Private object for locking instead of locking on a public type for SQL reliability work.
67         private static Object s_InternalSyncObject;
68         private static Object InternalSyncObject
69         {
70             get
71             {
72                 if (s_InternalSyncObject == null)
73                 {
74                     Object o = new Object();
75                     Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
76                 }
77                 return s_InternalSyncObject;
78             }
79         }
80
81         // Constructor
82         public InternalEncoderBestFitFallbackBuffer(InternalEncoderBestFitFallback fallback)
83         {
84             _oFallback = fallback;
85
86             if (_oFallback._arrayBestFit == null)
87             {
88                 // Lock so we don't confuse ourselves.
89                 lock (InternalSyncObject)
90                 {
91                     // Double check before we do it again.
92                     if (_oFallback._arrayBestFit == null)
93                         _oFallback._arrayBestFit = fallback._encoding.GetBestFitUnicodeToBytesData();
94                 }
95             }
96         }
97
98         // Fallback methods
99         public override bool Fallback(char charUnknown, int index)
100         {
101             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
102             // character in our array.
103             // Shouldn't be able to get here for all of our code pages, table would have to be messed up.
104             Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(non surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback");
105
106             _iCount = _iSize = 1;
107             _cBestFit = TryBestFit(charUnknown);
108             if (_cBestFit == '\0')
109                 _cBestFit = '?';
110
111             return true;
112         }
113
114         public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
115         {
116             // Double check input surrogate pair
117             if (!Char.IsHighSurrogate(charUnknownHigh))
118                 throw new ArgumentOutOfRangeException(nameof(charUnknownHigh),
119                     SR.Format(SR.ArgumentOutOfRange_Range,
120                     0xD800, 0xDBFF));
121
122             if (!Char.IsLowSurrogate(charUnknownLow))
123                 throw new ArgumentOutOfRangeException(nameof(charUnknownLow),
124                     SR.Format(SR.ArgumentOutOfRange_Range,
125                     0xDC00, 0xDFFF));
126             Contract.EndContractBlock();
127
128             // If we had a buffer already we're being recursive, throw, it's probably at the suspect
129             // character in our array.  0 is processing last character, < 0 is not falling back
130             // Shouldn't be able to get here, table would have to be messed up.
131             Debug.Assert(_iCount < 1, "[InternalEncoderBestFitFallbackBuffer.Fallback(surrogate)] Fallback char " + ((int)_cBestFit).ToString("X4", CultureInfo.InvariantCulture) + " caused recursive fallback");
132
133             // Go ahead and get our fallback, surrogates don't have best fit
134             _cBestFit = '?';
135             _iCount = _iSize = 2;
136
137             return true;
138         }
139
140         // Default version is overridden in EncoderReplacementFallback.cs
141         public override char GetNextChar()
142         {
143             // We want it to get < 0 because == 0 means that the current/last character is a fallback
144             // and we need to detect recursion.  We could have a flag but we already have this counter.
145             _iCount--;
146
147             // Do we have anything left? 0 is now last fallback char, negative is nothing left
148             if (_iCount < 0)
149                 return '\0';
150
151             // Need to get it out of the buffer.
152             // Make sure it didn't wrap from the fast count-- path
153             if (_iCount == int.MaxValue)
154             {
155                 _iCount = -1;
156                 return '\0';
157             }
158
159             // Return the best fit character
160             return _cBestFit;
161         }
162
163         public override bool MovePrevious()
164         {
165             // Exception fallback doesn't have anywhere to back up to.
166             if (_iCount >= 0)
167                 _iCount++;
168
169             // Return true if we could do it.
170             return (_iCount >= 0 && _iCount <= _iSize);
171         }
172
173
174         // How many characters left to output?
175         public override int Remaining
176         {
177             get
178             {
179                 return (_iCount > 0) ? _iCount : 0;
180             }
181         }
182
183         // Clear the buffer
184         public override unsafe void Reset()
185         {
186             _iCount = -1;
187             charStart = null;
188             bFallingBack = false;
189         }
190
191         // private helper methods
192         private char TryBestFit(char cUnknown)
193         {
194             // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array
195             int lowBound = 0;
196             int highBound = _oFallback._arrayBestFit.Length;
197             int index;
198
199             // Binary search the array
200             int iDiff;
201             while ((iDiff = (highBound - lowBound)) > 6)
202             {
203                 // Look in the middle, which is complicated by the fact that we have 2 #s for each pair,
204                 // so we don't want index to be odd because we want to be on word boundaries.
205                 // Also note that index can never == highBound (because diff is rounded down)
206                 index = ((iDiff / 2) + lowBound) & 0xFFFE;
207
208                 char cTest = _oFallback._arrayBestFit[index];
209                 if (cTest == cUnknown)
210                 {
211                     // We found it
212                     Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length,
213                         "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
214                     return _oFallback._arrayBestFit[index + 1];
215                 }
216                 else if (cTest < cUnknown)
217                 {
218                     // We weren't high enough
219                     lowBound = index;
220                 }
221                 else
222                 {
223                     // We weren't low enough
224                     highBound = index;
225                 }
226             }
227
228             for (index = lowBound; index < highBound; index += 2)
229             {
230                 if (_oFallback._arrayBestFit[index] == cUnknown)
231                 {
232                     // We found it
233                     Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length,
234                         "[InternalEncoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
235                     return _oFallback._arrayBestFit[index + 1];
236                 }
237             }
238
239             // Char wasn't in our table
240             return '\0';
241         }
242     }
243 }
244