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