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. 4 5 using System.Diagnostics; 6 7 namespace System.Text 8 { 9 public sealed class DecoderReplacementFallback : DecoderFallback 10 { 11 // Our variables 12 private String _strDefault; 13 14 // Construction. Default replacement fallback uses no best fit and ? replacement string DecoderReplacementFallback()15 public DecoderReplacementFallback() : this("?") 16 { 17 } 18 DecoderReplacementFallback(String replacement)19 public DecoderReplacementFallback(String replacement) 20 { 21 if (replacement == null) 22 throw new ArgumentNullException(nameof(replacement)); 23 24 // Make sure it doesn't have bad surrogate pairs 25 bool bFoundHigh = false; 26 for (int i = 0; i < replacement.Length; i++) 27 { 28 // Found a surrogate? 29 if (Char.IsSurrogate(replacement, i)) 30 { 31 // High or Low? 32 if (Char.IsHighSurrogate(replacement, i)) 33 { 34 // if already had a high one, stop 35 if (bFoundHigh) 36 break; // break & throw at the bFoundHIgh below 37 bFoundHigh = true; 38 } 39 else 40 { 41 // Low, did we have a high? 42 if (!bFoundHigh) 43 { 44 // Didn't have one, make if fail when we stop 45 bFoundHigh = true; 46 break; 47 } 48 49 // Clear flag 50 bFoundHigh = false; 51 } 52 } 53 // If last was high we're in trouble (not surrogate so not low surrogate, so break) 54 else if (bFoundHigh) 55 break; 56 } 57 if (bFoundHigh) 58 throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement))); 59 60 _strDefault = replacement; 61 } 62 63 public String DefaultString 64 { 65 get 66 { 67 return _strDefault; 68 } 69 } 70 CreateFallbackBuffer()71 public override DecoderFallbackBuffer CreateFallbackBuffer() 72 { 73 return new DecoderReplacementFallbackBuffer(this); 74 } 75 76 // Maximum number of characters that this instance of this fallback could return 77 public override int MaxCharCount 78 { 79 get 80 { 81 return _strDefault.Length; 82 } 83 } 84 Equals(Object value)85 public override bool Equals(Object value) 86 { 87 DecoderReplacementFallback that = value as DecoderReplacementFallback; 88 if (that != null) 89 { 90 return (_strDefault == that._strDefault); 91 } 92 return (false); 93 } 94 GetHashCode()95 public override int GetHashCode() 96 { 97 return _strDefault.GetHashCode(); 98 } 99 } 100 101 102 103 public sealed class DecoderReplacementFallbackBuffer : DecoderFallbackBuffer 104 { 105 // Store our default string 106 private String _strDefault; 107 private int _fallbackCount = -1; 108 private int _fallbackIndex = -1; 109 110 // Construction DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback)111 public DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback) 112 { 113 _strDefault = fallback.DefaultString; 114 } 115 116 // Fallback Methods Fallback(byte[] bytesUnknown, int index)117 public override bool Fallback(byte[] bytesUnknown, int index) 118 { 119 // We expect no previous fallback in our buffer 120 // We can't call recursively but others might (note, we don't test on last char!!!) 121 if (_fallbackCount >= 1) 122 { 123 ThrowLastBytesRecursive(bytesUnknown); 124 } 125 126 // Go ahead and get our fallback 127 if (_strDefault.Length == 0) 128 return false; 129 130 _fallbackCount = _strDefault.Length; 131 _fallbackIndex = -1; 132 133 return true; 134 } 135 GetNextChar()136 public override char GetNextChar() 137 { 138 // We want it to get < 0 because == 0 means that the current/last character is a fallback 139 // and we need to detect recursion. We could have a flag but we already have this counter. 140 _fallbackCount--; 141 _fallbackIndex++; 142 143 // Do we have anything left? 0 is now last fallback char, negative is nothing left 144 if (_fallbackCount < 0) 145 return '\0'; 146 147 // Need to get it out of the buffer. 148 // Make sure it didn't wrap from the fast count-- path 149 if (_fallbackCount == int.MaxValue) 150 { 151 _fallbackCount = -1; 152 return '\0'; 153 } 154 155 // Now make sure its in the expected range 156 Debug.Assert(_fallbackIndex < _strDefault.Length && _fallbackIndex >= 0, 157 "Index exceeds buffer range"); 158 159 return _strDefault[_fallbackIndex]; 160 } 161 MovePrevious()162 public override bool MovePrevious() 163 { 164 // Back up one, only if we just processed the last character (or earlier) 165 if (_fallbackCount >= -1 && _fallbackIndex >= 0) 166 { 167 _fallbackIndex--; 168 _fallbackCount++; 169 return true; 170 } 171 172 // Return false 'cause we couldn't do it. 173 return false; 174 } 175 176 // How many characters left to output? 177 public override int Remaining 178 { 179 get 180 { 181 // Our count is 0 for 1 character left. 182 return (_fallbackCount < 0) ? 0 : _fallbackCount; 183 } 184 } 185 186 // Clear the buffer Reset()187 public override unsafe void Reset() 188 { 189 _fallbackCount = -1; 190 _fallbackIndex = -1; 191 byteStart = null; 192 } 193 194 // This version just counts the fallback and doesn't actually copy anything. InternalFallback(byte[] bytes, byte* pBytes)195 internal unsafe override 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* 198 { 199 // return our replacement string Length 200 return _strDefault.Length; 201 } 202 } 203 } 204 205