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