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.
9 using System.Threading;
10 using System.Globalization;
11 using System.Diagnostics;
12 using System.Diagnostics.Contracts;
16 public abstract class DecoderFallback
18 internal bool bIsMicrosoftBestFitFallback = false;
20 private static volatile DecoderFallback replacementFallback; // Default fallback, uses no best fit & "?"
21 private static volatile DecoderFallback exceptionFallback;
23 // Private object for locking instead of locking on a internal type for SQL reliability work.
24 private static Object s_InternalSyncObject;
25 private static Object InternalSyncObject
29 if (s_InternalSyncObject == null)
31 Object o = new Object();
32 Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
34 return s_InternalSyncObject;
38 // Get each of our generic fallbacks.
40 public static DecoderFallback ReplacementFallback
44 if (replacementFallback == null)
45 lock (InternalSyncObject)
46 if (replacementFallback == null)
47 replacementFallback = new DecoderReplacementFallback();
49 return replacementFallback;
54 public static DecoderFallback ExceptionFallback
58 if (exceptionFallback == null)
59 lock (InternalSyncObject)
60 if (exceptionFallback == null)
61 exceptionFallback = new DecoderExceptionFallback();
63 return exceptionFallback;
69 // Return the appropriate unicode string alternative to the character that need to fall back.
70 // Most implimentations will be:
71 // return new MyCustomDecoderFallbackBuffer(this);
73 public abstract DecoderFallbackBuffer CreateFallbackBuffer();
75 // Maximum number of characters that this instance of this fallback could return
77 public abstract int MaxCharCount { get; }
81 public abstract class DecoderFallbackBuffer
83 // Most implimentations will probably need an implimenation-specific constructor
85 // internal methods that cannot be overriden that let us do our fallback thing
86 // These wrap the internal methods so that we can check for people doing stuff that's incorrect
88 public abstract bool Fallback(byte[] bytesUnknown, int index);
92 public abstract char GetNextChar();
94 // Back up a character
96 public abstract bool MovePrevious();
98 // How many chars left in this fallback?
100 public abstract int Remaining { get; }
104 public virtual void Reset()
106 while (GetNextChar() != (char)0) ;
109 // Internal items to help us figure out what we're doing as far as error messages, etc.
110 // These help us with our performance and messages internally
111 internal unsafe byte* byteStart;
112 internal unsafe char* charEnd;
115 internal unsafe void InternalReset()
121 // Set the above values
122 // This can't be part of the constructor because DecoderFallbacks would have to know how to impliment these.
123 internal unsafe void InternalInitialize(byte* byteStart, char* charEnd)
125 this.byteStart = byteStart;
126 this.charEnd = charEnd;
129 // Fallback the current byte by sticking it into the remaining char buffer.
130 // This can only be called by our encodings (other have to use the public fallback methods), so
131 // we can use our DecoderNLS here too (except we don't).
132 // Returns true if we are successful, false if we can't fallback the character (no buffer space)
133 // So caller needs to throw buffer space if return false.
134 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
135 // array, and we might need the index, hence the byte*
136 // Don't touch ref chars unless we succeed
137 internal unsafe virtual bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars)
139 // Copy bytes to array (slow, but right now that's what we get to do.
140 // byte[] bytesUnknown = new byte[count];
141 // for (int i = 0; i < count; i++)
142 // bytesUnknown[i] = *(bytes++);
144 Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
146 // See if there's a fallback character and we have an output buffer then copy our string.
147 if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
149 // Copy the chars to our output
151 char* charTemp = chars;
152 bool bHighSurrogate = false;
153 while ((ch = GetNextChar()) != 0)
155 // Make sure no mixed up surrogates
156 if (Char.IsSurrogate(ch))
158 if (Char.IsHighSurrogate(ch))
162 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
163 bHighSurrogate = true;
168 if (bHighSurrogate == false)
169 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
170 bHighSurrogate = false;
174 if (charTemp >= charEnd)
183 // Need to make sure that bHighSurrogate isn't true
185 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
187 // Now we aren't going to be false, so its OK to update chars
194 // This version just counts the fallback and doesn't actually copy anything.
195 internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes)
196 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
197 // array, and we might need the index, hence the byte*
199 // Copy bytes to array (slow, but right now that's what we get to do.
200 // byte[] bytesUnknown = new byte[count];
201 // for (int i = 0; i < count; i++)
202 // bytesUnknown[i] = *(bytes++);
204 Debug.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
206 // See if there's a fallback character and we have an output buffer then copy our string.
207 if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
212 bool bHighSurrogate = false;
213 while ((ch = GetNextChar()) != 0)
215 // Make sure no mixed up surrogates
216 if (Char.IsSurrogate(ch))
218 if (Char.IsHighSurrogate(ch))
222 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
223 bHighSurrogate = true;
228 if (bHighSurrogate == false)
229 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
230 bHighSurrogate = false;
237 // Need to make sure that bHighSurrogate isn't true
239 throw new ArgumentException(SR.Argument_InvalidCharSequenceNoIndex);
244 // If no fallback return 0
248 // private helper methods
249 internal void ThrowLastBytesRecursive(byte[] bytesUnknown)
251 // Create a string representation of our bytes.
252 StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3);
254 for (i = 0; i < bytesUnknown.Length && i < 20; i++)
256 if (strBytes.Length > 0)
257 strBytes.Append(" ");
258 strBytes.Append(String.Format(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]));
260 // In case the string's really long
262 strBytes.Append(" ...");
264 // Throw it, using our complete bytes
265 throw new ArgumentException(
266 SR.Format(SR.Argument_RecursiveFallbackBytes, strBytes.ToString()), nameof(bytesUnknown));