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.
11 using System.Threading;
12 using System.Diagnostics;
13 using System.Diagnostics.Contracts;
17 internal sealed class InternalDecoderBestFitFallback : DecoderFallback
20 internal Encoding encoding = null;
21 internal char[] arrayBestFit = null;
22 internal char cReplacement = '?';
24 internal InternalDecoderBestFitFallback(Encoding encoding)
26 // Need to load our replacement characters table.
27 this.encoding = encoding;
28 this.bIsMicrosoftBestFitFallback = true;
31 public override DecoderFallbackBuffer CreateFallbackBuffer()
33 return new InternalDecoderBestFitFallbackBuffer(this);
36 // Maximum number of characters that this instance of this fallback could return
37 public override int MaxCharCount
45 public override bool Equals(Object value)
47 InternalDecoderBestFitFallback that = value as InternalDecoderBestFitFallback;
50 return (this.encoding.CodePage == that.encoding.CodePage);
55 public override int GetHashCode()
57 return this.encoding.CodePage;
61 internal sealed class InternalDecoderBestFitFallbackBuffer : DecoderFallbackBuffer
64 internal char cBestFit = '\0';
65 internal int iCount = -1;
67 private InternalDecoderBestFitFallback oFallback;
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
75 if (s_InternalSyncObject == null)
77 Object o = new Object();
78 Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
80 return s_InternalSyncObject;
85 public InternalDecoderBestFitFallbackBuffer(InternalDecoderBestFitFallback fallback)
89 if (oFallback.arrayBestFit == null)
91 // Lock so we don't confuse ourselves.
92 lock (InternalSyncObject)
94 // Double check before we do it again.
95 if (oFallback.arrayBestFit == null)
96 oFallback.arrayBestFit = fallback.encoding.GetBestFitBytesToUnicodeData();
102 public override bool Fallback(byte[] bytesUnknown, int index)
104 // We expect no previous fallback in our buffer
105 Debug.Assert(iCount < 1, "[DecoderReplacementFallbackBuffer.Fallback] Calling fallback without a previously empty buffer");
107 cBestFit = TryBestFit(bytesUnknown);
108 if (cBestFit == '\0')
109 cBestFit = oFallback.cReplacement;
116 // Default version is overridden in DecoderReplacementFallback.cs
117 public override char GetNextChar()
119 // We want it to get < 0 because == 0 means that the current/last character is a fallback
120 // and we need to detect recursion. We could have a flag but we already have this counter.
123 // Do we have anything left? 0 is now last fallback char, negative is nothing left
127 // Need to get it out of the buffer.
128 // Make sure it didn't wrap from the fast count-- path
129 if (iCount == int.MaxValue)
135 // Return the best fit character
139 public override bool MovePrevious()
141 // Exception fallback doesn't have anywhere to back up to.
145 // Return true if we could do it.
146 return (iCount >= 0 && iCount <= iSize);
149 // How many characters left to output?
150 public override int Remaining
154 return (iCount > 0) ? iCount : 0;
159 public override unsafe void Reset()
165 // This version just counts the fallback and doesn't actually copy anything.
166 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
167 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
168 // array, and we might need the index, hence the byte*
170 // return our replacement string Length (always 1 for InternalDecoderBestFitFallback, either
171 // a best fit char or ?
175 // private helper methods
176 private char TryBestFit(byte[] bytesCheck)
178 // Need to figure out our best fit character, low is beginning of array, high is 1 AFTER end of array
180 int highBound = oFallback.arrayBestFit.Length;
184 // Check trivial case first (no best fit)
188 // If our array is too small or too big we can't check
189 if (bytesCheck.Length == 0 || bytesCheck.Length > 2)
192 if (bytesCheck.Length == 1)
193 cCheck = unchecked((char)bytesCheck[0]);
195 cCheck = unchecked((char)((bytesCheck[0] << 8) + bytesCheck[1]));
197 // Check trivial out of range case
198 if (cCheck < oFallback.arrayBestFit[0] || cCheck > oFallback.arrayBestFit[highBound - 2])
201 // Binary search the array
203 while ((iDiff = (highBound - lowBound)) > 6)
205 // Look in the middle, which is complicated by the fact that we have 2 #s for each pair,
206 // so we don't want index to be odd because it must be word aligned.
207 // Also note that index can never == highBound (because diff is rounded down)
208 index = ((iDiff / 2) + lowBound) & 0xFFFE;
210 char cTest = oFallback.arrayBestFit[index];
214 Debug.Assert(index + 1 < oFallback.arrayBestFit.Length,
215 "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
216 return oFallback.arrayBestFit[index + 1];
218 else if (cTest < cCheck)
220 // We weren't high enough
225 // We weren't low enough
230 for (index = lowBound; index < highBound; index += 2)
232 if (oFallback.arrayBestFit[index] == cCheck)
235 Debug.Assert(index + 1 < oFallback.arrayBestFit.Length,
236 "[InternalDecoderBestFitFallbackBuffer.TryBestFit]Expected replacement character at end of array");
237 return oFallback.arrayBestFit[index + 1];
241 // Char wasn't in our table