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.Diagnostics;
8 using System.Diagnostics.Contracts;
12 public sealed class EncoderReplacementFallback : EncoderFallback
15 private String _strDefault;
17 // Construction. Default replacement fallback uses no best fit and ? replacement string
18 public EncoderReplacementFallback() : this("?")
22 public EncoderReplacementFallback(String replacement)
25 if (replacement == null)
26 throw new ArgumentNullException(nameof(replacement));
27 Contract.EndContractBlock();
29 // Make sure it doesn't have bad surrogate pairs
30 bool bFoundHigh = false;
31 for (int i = 0; i < replacement.Length; i++)
34 if (Char.IsSurrogate(replacement, i))
37 if (Char.IsHighSurrogate(replacement, i))
39 // if already had a high one, stop
41 break; // break & throw at the bFoundHIgh below
46 // Low, did we have a high?
49 // Didn't have one, make if fail when we stop
58 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
63 throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)));
65 _strDefault = replacement;
68 public String DefaultString
76 public override EncoderFallbackBuffer CreateFallbackBuffer()
78 return new EncoderReplacementFallbackBuffer(this);
81 // Maximum number of characters that this instance of this fallback could return
82 public override int MaxCharCount
86 return _strDefault.Length;
90 public override bool Equals(Object value)
92 EncoderReplacementFallback that = value as EncoderReplacementFallback;
95 return (_strDefault == that._strDefault);
100 public override int GetHashCode()
102 return _strDefault.GetHashCode();
108 public sealed class EncoderReplacementFallbackBuffer : EncoderFallbackBuffer
110 // Store our default string
111 private String _strDefault;
112 private int _fallbackCount = -1;
113 private int _fallbackIndex = -1;
116 public EncoderReplacementFallbackBuffer(EncoderReplacementFallback fallback)
118 // 2X in case we're a surrogate pair
119 _strDefault = fallback.DefaultString + fallback.DefaultString;
123 public override bool Fallback(char charUnknown, int index)
125 // If we had a buffer already we're being recursive, throw, it's probably at the suspect
126 // character in our array.
127 if (_fallbackCount >= 1)
129 // If we're recursive we may still have something in our buffer that makes this a surrogate
130 if (char.IsHighSurrogate(charUnknown) && _fallbackCount >= 0 &&
131 char.IsLowSurrogate(_strDefault[_fallbackIndex + 1]))
132 ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknown, _strDefault[_fallbackIndex + 1]));
134 // Nope, just one character
135 ThrowLastCharRecursive(unchecked((int)charUnknown));
138 // Go ahead and get our fallback
139 // Divide by 2 because we aren't a surrogate pair
140 _fallbackCount = _strDefault.Length / 2;
143 return _fallbackCount != 0;
146 public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index)
148 // Double check input surrogate pair
149 if (!Char.IsHighSurrogate(charUnknownHigh))
150 throw new ArgumentOutOfRangeException(nameof(charUnknownHigh),
151 SR.Format(SR.ArgumentOutOfRange_Range, 0xD800, 0xDBFF));
153 if (!Char.IsLowSurrogate(charUnknownLow))
154 throw new ArgumentOutOfRangeException(nameof(charUnknownLow),
155 SR.Format(SR.ArgumentOutOfRange_Range, 0xDC00, 0xDFFF));
156 Contract.EndContractBlock();
158 // If we had a buffer already we're being recursive, throw, it's probably at the suspect
159 // character in our array.
160 if (_fallbackCount >= 1)
161 ThrowLastCharRecursive(Char.ConvertToUtf32(charUnknownHigh, charUnknownLow));
163 // Go ahead and get our fallback
164 _fallbackCount = _strDefault.Length;
167 return _fallbackCount != 0;
170 public override char GetNextChar()
172 // We want it to get < 0 because == 0 means that the current/last character is a fallback
173 // and we need to detect recursion. We could have a flag but we already have this counter.
177 // Do we have anything left? 0 is now last fallback char, negative is nothing left
178 if (_fallbackCount < 0)
181 // Need to get it out of the buffer.
182 // Make sure it didn't wrap from the fast count-- path
183 if (_fallbackCount == int.MaxValue)
189 // Now make sure its in the expected range
190 Debug.Assert(_fallbackIndex < _strDefault.Length && _fallbackIndex >= 0,
191 "Index exceeds buffer range");
193 return _strDefault[_fallbackIndex];
196 public override bool MovePrevious()
198 // Back up one, only if we just processed the last character (or earlier)
199 if (_fallbackCount >= -1 && _fallbackIndex >= 0)
206 // Return false 'cause we couldn't do it.
210 // How many characters left to output?
211 public override int Remaining
215 // Our count is 0 for 1 character left.
216 return (_fallbackCount < 0) ? 0 : _fallbackCount;
221 public override unsafe void Reset()
226 bFallingBack = false;