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.
7 using System.Threading;
8 using System.Diagnostics;
9 using System.Diagnostics.Contracts;
13 public abstract class EncoderFallback
15 // disable csharp compiler warning #0414: field assigned unused value
16 #pragma warning disable 0414
17 internal bool bIsMicrosoftBestFitFallback = false;
18 #pragma warning restore 0414
20 private static volatile EncoderFallback replacementFallback; // Default fallback, uses no best fit & "?"
21 private static volatile EncoderFallback exceptionFallback;
23 // Private object for locking instead of locking on a public 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 EncoderFallback ReplacementFallback
44 if (replacementFallback == null)
45 lock (InternalSyncObject)
46 if (replacementFallback == null)
47 replacementFallback = new EncoderReplacementFallback();
49 return replacementFallback;
54 public static EncoderFallback ExceptionFallback
58 if (exceptionFallback == null)
59 lock (InternalSyncObject)
60 if (exceptionFallback == null)
61 exceptionFallback = new EncoderExceptionFallback();
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 MyCustomEncoderFallbackBuffer(this);
73 public abstract EncoderFallbackBuffer CreateFallbackBuffer();
75 // Maximum number of characters that this instance of this fallback could return
77 public abstract int MaxCharCount { get; }
81 public abstract class EncoderFallbackBuffer
83 // Most implementations will probably need an implemenation-specific constructor
85 // Public 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 is incorrect
88 public abstract bool Fallback(char charUnknown, int index);
90 public abstract bool Fallback(char charUnknownHigh, char charUnknownLow, int index);
94 public abstract char GetNextChar();
96 // Back up a character
98 public abstract bool MovePrevious();
100 // How many chars left in this fallback?
102 public abstract int Remaining { get; }
104 // Not sure if this should be public or not.
107 public virtual void Reset()
109 while (GetNextChar() != (char)0) ;
112 // Internal items to help us figure out what we're doing as far as error messages, etc.
113 // These help us with our performance and messages internally
114 internal unsafe char* charStart;
115 internal unsafe char* charEnd;
116 internal EncoderNLS encoder;
117 internal bool setEncoder;
118 internal bool bUsedEncoder;
119 internal bool bFallingBack = false;
120 internal int iRecursionCount = 0;
121 private const int iMaxRecursion = 250;
124 // For example, what if someone fails a conversion and wants to reset one of our fallback buffers?
125 internal unsafe void InternalReset()
128 bFallingBack = false;
133 // Set the above values
134 // This can't be part of the constructor because EncoderFallbacks would have to know how to impliment these.
135 internal unsafe void InternalInitialize(char* charStart, char* charEnd, EncoderNLS encoder, bool setEncoder)
137 this.charStart = charStart;
138 this.charEnd = charEnd;
139 this.encoder = encoder;
140 this.setEncoder = setEncoder;
141 this.bUsedEncoder = false;
142 this.bFallingBack = false;
143 this.iRecursionCount = 0;
146 internal char InternalGetNextChar()
148 char ch = GetNextChar();
149 bFallingBack = (ch != 0);
150 if (ch == 0) iRecursionCount = 0;
154 // Fallback the current character using the remaining buffer and encoder if necessary
155 // This can only be called by our encodings (other have to use the public fallback methods), so
156 // we can use our EncoderNLS here too.
157 // setEncoder is true if we're calling from a GetBytes method, false if we're calling from a GetByteCount
159 // Note that this could also change the contents of this.encoder, which is the same
160 // object that the caller is using, so the caller could mess up the encoder for us
161 // if they aren't careful.
162 internal unsafe virtual bool InternalFallback(char ch, ref char* chars)
164 // Shouldn't have null charStart
165 Debug.Assert(charStart != null,
166 "[EncoderFallback.InternalFallbackBuffer]Fallback buffer is not initialized");
168 // Get our index, remember chars was preincremented to point at next char, so have to -1
169 int index = (int)(chars - charStart) - 1;
171 // See if it was a high surrogate
172 if (Char.IsHighSurrogate(ch))
174 // See if there's a low surrogate to go with it
175 if (chars >= this.charEnd)
177 // Nothing left in input buffer
178 // No input, return 0 if mustflush is false
179 if (this.encoder != null && !this.encoder.MustFlush)
181 // Done, nothing to fallback
185 this.encoder.charLeftOver = ch;
187 bFallingBack = false;
193 // Might have a low surrogate
195 if (Char.IsLowSurrogate(cNext))
197 // If already falling back then fail
198 if (bFallingBack && iRecursionCount++ > iMaxRecursion)
199 ThrowLastCharRecursive(Char.ConvertToUtf32(ch, cNext));
201 // Next is a surrogate, add it as surrogate pair, and increment chars
203 bFallingBack = Fallback(ch, cNext, index);
207 // Next isn't a low surrogate, just fallback the high surrogate
211 // If already falling back then fail
212 if (bFallingBack && iRecursionCount++ > iMaxRecursion)
213 ThrowLastCharRecursive((int)ch);
215 // Fall back our char
216 bFallingBack = Fallback(ch, index);
221 // private helper methods
222 internal void ThrowLastCharRecursive(int charRecursive)
224 // Throw it, using our complete character
225 throw new ArgumentException(
226 SR.Format(SR.Argument_RecursiveFallback, charRecursive), "chars");