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.
5 using System.Diagnostics;
6 using System.Diagnostics.Contracts;
10 public sealed class DecoderReplacementFallback : DecoderFallback
13 private String _strDefault;
15 // Construction. Default replacement fallback uses no best fit and ? replacement string
16 public DecoderReplacementFallback() : this("?")
20 public DecoderReplacementFallback(String replacement)
22 if (replacement == null)
23 throw new ArgumentNullException(nameof(replacement));
24 Contract.EndContractBlock();
26 // Make sure it doesn't have bad surrogate pairs
27 bool bFoundHigh = false;
28 for (int i = 0; i < replacement.Length; i++)
31 if (Char.IsSurrogate(replacement, i))
34 if (Char.IsHighSurrogate(replacement, i))
36 // if already had a high one, stop
38 break; // break & throw at the bFoundHIgh below
43 // Low, did we have a high?
46 // Didn't have one, make if fail when we stop
55 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
60 throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)));
62 _strDefault = replacement;
65 public String DefaultString
73 public override DecoderFallbackBuffer CreateFallbackBuffer()
75 return new DecoderReplacementFallbackBuffer(this);
78 // Maximum number of characters that this instance of this fallback could return
79 public override int MaxCharCount
83 return _strDefault.Length;
87 public override bool Equals(Object value)
89 DecoderReplacementFallback that = value as DecoderReplacementFallback;
92 return (_strDefault == that._strDefault);
97 public override int GetHashCode()
99 return _strDefault.GetHashCode();
105 public sealed class DecoderReplacementFallbackBuffer : DecoderFallbackBuffer
107 // Store our default string
108 private String _strDefault;
109 private int _fallbackCount = -1;
110 private int _fallbackIndex = -1;
113 public DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback)
115 _strDefault = fallback.DefaultString;
119 public override bool Fallback(byte[] bytesUnknown, int index)
121 // We expect no previous fallback in our buffer
122 // We can't call recursively but others might (note, we don't test on last char!!!)
123 if (_fallbackCount >= 1)
125 ThrowLastBytesRecursive(bytesUnknown);
128 // Go ahead and get our fallback
129 if (_strDefault.Length == 0)
132 _fallbackCount = _strDefault.Length;
138 public override char GetNextChar()
140 // We want it to get < 0 because == 0 means that the current/last character is a fallback
141 // and we need to detect recursion. We could have a flag but we already have this counter.
145 // Do we have anything left? 0 is now last fallback char, negative is nothing left
146 if (_fallbackCount < 0)
149 // Need to get it out of the buffer.
150 // Make sure it didn't wrap from the fast count-- path
151 if (_fallbackCount == int.MaxValue)
157 // Now make sure its in the expected range
158 Debug.Assert(_fallbackIndex < _strDefault.Length && _fallbackIndex >= 0,
159 "Index exceeds buffer range");
161 return _strDefault[_fallbackIndex];
164 public override bool MovePrevious()
166 // Back up one, only if we just processed the last character (or earlier)
167 if (_fallbackCount >= -1 && _fallbackIndex >= 0)
174 // Return false 'cause we couldn't do it.
178 // How many characters left to output?
179 public override int Remaining
183 // Our count is 0 for 1 character left.
184 return (_fallbackCount < 0) ? 0 : _fallbackCount;
189 public override unsafe void Reset()
196 // This version just counts the fallback and doesn't actually copy anything.
197 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
198 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
199 // array, and we might need the index, hence the byte*
201 // return our replacement string Length
202 return _strDefault.Length;