1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 //
7 using System;
8 using System.Security;
9 using System.Threading;
10 using System.Globalization;
11 using System.Diagnostics.Contracts;
12 
13 namespace System.Text
14 {
15     [Serializable]
16     public abstract class DecoderFallback
17     {
18         internal bool                  bIsMicrosoftBestFitFallback = false;
19 
20         private static volatile DecoderFallback replacementFallback; // Default fallback, uses no best fit & "?"
21         private static volatile DecoderFallback exceptionFallback;
22 
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
26         {
27             get
28             {
29                 if (s_InternalSyncObject == null)
30                 {
31                     Object o = new Object();
32                     Interlocked.CompareExchange<Object>(ref s_InternalSyncObject, o, null);
33                 }
34                 return s_InternalSyncObject;
35             }
36         }
37 
38         // Get each of our generic fallbacks.
39 
40         public static DecoderFallback ReplacementFallback
41         {
42             get
43             {
44                 if (replacementFallback == null)
45                     lock(InternalSyncObject)
46                         if (replacementFallback == null)
47                             replacementFallback = new DecoderReplacementFallback();
48 
49                 return replacementFallback;
50             }
51         }
52 
53 
54         public static DecoderFallback ExceptionFallback
55         {
56             get
57             {
58                 if (exceptionFallback == null)
59                     lock(InternalSyncObject)
60                         if (exceptionFallback == null)
61                             exceptionFallback = new DecoderExceptionFallback();
62 
63                 return exceptionFallback;
64             }
65         }
66 
67         // Fallback
68         //
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);
72 
CreateFallbackBuffer()73         public abstract DecoderFallbackBuffer CreateFallbackBuffer();
74 
75         // Maximum number of characters that this instance of this fallback could return
76 
77         public abstract int MaxCharCount { get; }
78 
79         internal bool IsMicrosoftBestFitFallback
80         {
81             get
82             {
83                 return bIsMicrosoftBestFitFallback;
84             }
85         }
86     }
87 
88 
89     public abstract class DecoderFallbackBuffer
90     {
91         // Most implimentations will probably need an implimenation-specific constructor
92 
93         // internal methods that cannot be overriden that let us do our fallback thing
94         // These wrap the internal methods so that we can check for people doing stuff that's incorrect
95 
Fallback(byte[] bytesUnknown, int index)96         public abstract bool Fallback(byte[] bytesUnknown, int index);
97 
98         // Get next character
99 
GetNextChar()100         public abstract char GetNextChar();
101 
102         // Back up a character
103 
MovePrevious()104         public abstract bool MovePrevious();
105 
106         // How many chars left in this fallback?
107 
108         public abstract int Remaining { get; }
109 
110         // Clear the buffer
111 
Reset()112         public virtual void Reset()
113         {
114             while (GetNextChar() != (char)0);
115         }
116 
117         // Internal items to help us figure out what we're doing as far as error messages, etc.
118         // These help us with our performance and messages internally
119         [SecurityCritical]
120         internal     unsafe byte*    byteStart;
121         [SecurityCritical]
122         internal     unsafe char*    charEnd;
123 
124         // Internal Reset
125         [System.Security.SecurityCritical]  // auto-generated
InternalReset()126         internal unsafe void InternalReset()
127         {
128             byteStart = null;
129             Reset();
130         }
131 
132         // Set the above values
133         // This can't be part of the constructor because DecoderFallbacks would have to know how to impliment these.
134         [System.Security.SecurityCritical]  // auto-generated
InternalInitialize(byte* byteStart, char* charEnd)135         internal unsafe void InternalInitialize(byte* byteStart, char* charEnd)
136         {
137             this.byteStart = byteStart;
138             this.charEnd = charEnd;
139         }
140 
141         // Fallback the current byte by sticking it into the remaining char buffer.
142         // This can only be called by our encodings (other have to use the public fallback methods), so
143         // we can use our DecoderNLS here too (except we don't).
144         // Returns true if we are successful, false if we can't fallback the character (no buffer space)
145         // So caller needs to throw buffer space if return false.
146         // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
147         // array, and we might need the index, hence the byte*
148         // Don't touch ref chars unless we succeed
149         [System.Security.SecurityCritical]  // auto-generated
InternalFallback(byte[] bytes, byte* pBytes, ref char* chars)150         internal unsafe virtual bool InternalFallback(byte[] bytes, byte* pBytes, ref char* chars)
151         {
152             // Copy bytes to array (slow, but right now that's what we get to do.
153           //  byte[] bytesUnknown = new byte[count];
154 //            for (int i = 0; i < count; i++)
155 //                bytesUnknown[i] = *(bytes++);
156 
157             Contract.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
158 
159             // See if there's a fallback character and we have an output buffer then copy our string.
160             if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
161             {
162                 // Copy the chars to our output
163                 char ch;
164                 char* charTemp = chars;
165                 bool bHighSurrogate = false;
166                 while ((ch = GetNextChar()) != 0)
167                 {
168                     // Make sure no mixed up surrogates
169                     if (Char.IsSurrogate(ch))
170                     {
171                         if (Char.IsHighSurrogate(ch))
172                         {
173                             // High Surrogate
174                             if (bHighSurrogate)
175                                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
176                             bHighSurrogate = true;
177                         }
178                         else
179                         {
180                             // Low surrogate
181                             if (bHighSurrogate == false)
182                                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
183                             bHighSurrogate = false;
184                         }
185                     }
186 
187                     if (charTemp >= charEnd)
188                     {
189                         // No buffer space
190                         return false;
191                     }
192 
193                     *(charTemp++) = ch;
194                 }
195 
196                 // Need to make sure that bHighSurrogate isn't true
197                 if (bHighSurrogate)
198                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
199 
200                 // Now we aren't going to be false, so its OK to update chars
201                 chars = charTemp;
202             }
203 
204             return true;
205         }
206 
207         // This version just counts the fallback and doesn't actually copy anything.
208         [System.Security.SecurityCritical]  // auto-generated
InternalFallback(byte[] bytes, byte* pBytes)209         internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes)
210         // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
211         // array, and we might need the index, hence the byte*
212         {
213             // Copy bytes to array (slow, but right now that's what we get to do.
214 //            byte[] bytesUnknown = new byte[count];
215 //            for (int i = 0; i < count; i++)
216   //              bytesUnknown[i] = *(bytes++);
217 
218             Contract.Assert(byteStart != null, "[DecoderFallback.InternalFallback]Used InternalFallback without calling InternalInitialize");
219 
220             // See if there's a fallback character and we have an output buffer then copy our string.
221             if (this.Fallback(bytes, (int)(pBytes - byteStart - bytes.Length)))
222             {
223                 int count = 0;
224 
225                 char ch;
226                 bool bHighSurrogate = false;
227                 while ((ch = GetNextChar()) != 0)
228                 {
229                     // Make sure no mixed up surrogates
230                     if (Char.IsSurrogate(ch))
231                     {
232                         if (Char.IsHighSurrogate(ch))
233                         {
234                             // High Surrogate
235                             if (bHighSurrogate)
236                                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
237                             bHighSurrogate = true;
238                         }
239                         else
240                         {
241                             // Low surrogate
242                             if (bHighSurrogate == false)
243                                 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
244                             bHighSurrogate = false;
245                         }
246                     }
247 
248                     count++;
249                 }
250 
251                 // Need to make sure that bHighSurrogate isn't true
252                 if (bHighSurrogate)
253                     throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex"));
254 
255                 return count;
256             }
257 
258             // If no fallback return 0
259             return 0;
260         }
261 
262         // private helper methods
ThrowLastBytesRecursive(byte[] bytesUnknown)263         internal void ThrowLastBytesRecursive(byte[] bytesUnknown)
264         {
265             // Create a string representation of our bytes.
266             StringBuilder strBytes = new StringBuilder(bytesUnknown.Length * 3);
267             int i;
268             for (i = 0; i < bytesUnknown.Length && i < 20; i++)
269             {
270                 if (strBytes.Length > 0)
271                     strBytes.Append(" ");
272                 strBytes.Append(String.Format(CultureInfo.InvariantCulture, "\\x{0:X2}", bytesUnknown[i]));
273             }
274             // In case the string's really long
275             if (i == 20)
276                 strBytes.Append(" ...");
277 
278             // Throw it, using our complete bytes
279             throw new ArgumentException(
280                 Environment.GetResourceString("Argument_RecursiveFallbackBytes",
281                     strBytes.ToString()), "bytesUnknown");
282         }
283 
284     }
285 }
286