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.
6 // This is used internally to create best fit behavior as per the original windows best fit behavior.
9 using System.Diagnostics;
10 using System.Threading;
14 internal sealed class InternalDecoderBestFitFallback : DecoderFallback
17 internal Encoding _encoding = null;
18 internal char[] _arrayBestFit = null;
19 internal char _cReplacement = '?';
21 internal InternalDecoderBestFitFallback(Encoding encoding)
23 // Need to load our replacement characters table.
27 public override DecoderFallbackBuffer CreateFallbackBuffer()
29 return new InternalDecoderBestFitFallbackBuffer(this);
32 // Maximum number of characters that this instance of this fallback could return
33 public override int MaxCharCount
41 public override bool Equals(Object value)
43 InternalDecoderBestFitFallback that = value as InternalDecoderBestFitFallback;
46 return (_encoding.CodePage == that._encoding.CodePage);
51 public override int GetHashCode()
53 return _encoding.CodePage;
57 internal sealed class InternalDecoderBestFitFallbackBuffer : DecoderFallbackBuffer
60 private char _cBestFit = '\0';
61 private int _iCount = -1;
63 private InternalDecoderBestFitFallback _oFallback;
65 // Private object for locking instead of locking on a public type for SQL reliability work.
66 private static Object s_InternalSyncObject;
67 private static Object InternalSyncObject
71 if (s_InternalSyncObject == null)
73 Object o = new Object();
74 Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
76 return s_InternalSyncObject;
81 public InternalDecoderBestFitFallbackBuffer(InternalDecoderBestFitFallback fallback)
83 _oFallback = fallback;
85 if (_oFallback._arrayBestFit == null)
87 // Lock so we don't confuse ourselves.
88 lock (InternalSyncObject)
90 // Double check before we do it again.
91 if (_oFallback._arrayBestFit == null)
92 _oFallback._arrayBestFit = fallback._encoding.GetBestFitBytesToUnicodeData();
98 public override bool Fallback(byte[] bytesUnknown, int index)
100 // We expect no previous fallback in our buffer
101 Debug.Assert(_iCount < 1, "[DecoderReplacementFallbackBuffer.Fallback] Calling fallback without a previously empty buffer");
103 _cBestFit = TryBestFit(bytesUnknown);
104 if (_cBestFit == '\0')
105 _cBestFit = _oFallback._cReplacement;
107 _iCount = _iSize = 1;
112 // Default version is overridden in DecoderReplacementFallback.cs
113 public override char GetNextChar()
115 // We want it to get < 0 because == 0 means that the current/last character is a fallback
116 // and we need to detect recursion. We could have a flag but we already have this counter.
119 // Do we have anything left? 0 is now last fallback char, negative is nothing left
123 // Need to get it out of the buffer.
124 // Make sure it didn't wrap from the fast count-- path
125 if (_iCount == int.MaxValue)
131 // Return the best fit character
135 public override bool MovePrevious()
137 // Exception fallback doesn't have anywhere to back up to.
141 // Return true if we could do it.
142 return (_iCount >= 0 && _iCount <= _iSize);
145 // How many characters left to output?
146 public override int Remaining
150 return (_iCount > 0) ? _iCount : 0;
155 public override unsafe void Reset()
161 // This version just counts the fallback and doesn't actually copy anything.
162 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
163 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
164 // array, and we might need the index, hence the byte*
166 // return our replacement string Length (always 1 for InternalDecoderBestFitFallback, either
167 // a best fit char or ?
171 // private helper methods
172 private char TryBestFit(byte[] bytesCheck)
174 // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array
176 int highBound = _oFallback._arrayBestFit.Length;
180 // Check trivial case first (no best fit)
184 // If our array is too small or too big we can't check
185 if (bytesCheck.Length == 0 || bytesCheck.Length > 2)
188 if (bytesCheck.Length == 1)
189 cCheck = unchecked((char)bytesCheck[0]);
191 cCheck = unchecked((char)((bytesCheck[0] << 8) + bytesCheck[1]));
193 // Check trivial out of range case
194 if (cCheck < _oFallback._arrayBestFit[0] || cCheck > _oFallback._arrayBestFit[highBound - 2])
197 // Binary search the array
199 while ((iDiff = (highBound - lowBound)) > 6)
201 // Look in the middle, which is complicated by the fact that we have 2 #s for each pair,
202 // so we don't want index to be odd because it must be word aligned.
203 // Also note that index can never == highBound (because diff is rounded down)
204 index = ((iDiff / 2) + lowBound) & 0xFFFE;
206 char cTest = _oFallback._arrayBestFit[index];
210 Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length,
211 "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
212 return _oFallback._arrayBestFit[index + 1];
214 else if (cTest < cCheck)
216 // We weren't high enough
221 // We weren't low enough
226 for (index = lowBound; index < highBound; index += 2)
228 if (_oFallback._arrayBestFit[index] == cCheck)
231 Debug.Assert(index + 1 < _oFallback._arrayBestFit.Length,
232 "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
233 return _oFallback._arrayBestFit[index + 1];
237 // Char wasn't in our table