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.
6 using System.Diagnostics;
7 using System.Diagnostics.Contracts;
11 public sealed class DecoderReplacementFallback : DecoderFallback
14 private String strDefault;
16 // Construction. Default replacement fallback uses no best fit and ? replacement string
17 public DecoderReplacementFallback() : this("?")
21 public DecoderReplacementFallback(String replacement)
23 if (replacement == null)
24 throw new ArgumentNullException(nameof(replacement));
25 Contract.EndContractBlock();
27 // Make sure it doesn't have bad surrogate pairs
28 bool bFoundHigh = false;
29 for (int i = 0; i < replacement.Length; i++)
32 if (Char.IsSurrogate(replacement, i))
35 if (Char.IsHighSurrogate(replacement, i))
37 // if already had a high one, stop
39 break; // break & throw at the bFoundHIgh below
44 // Low, did we have a high?
47 // Didn't have one, make if fail when we stop
56 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
61 throw new ArgumentException(SR.Format(SR.Argument_InvalidCharSequenceNoIndex, nameof(replacement)));
63 strDefault = replacement;
66 public String DefaultString
74 public override DecoderFallbackBuffer CreateFallbackBuffer()
76 return new DecoderReplacementFallbackBuffer(this);
79 // Maximum number of characters that this instance of this fallback could return
80 public override int MaxCharCount
84 return strDefault.Length;
88 public override bool Equals(Object value)
90 DecoderReplacementFallback that = value as DecoderReplacementFallback;
93 return (strDefault == that.strDefault);
98 public override int GetHashCode()
100 return strDefault.GetHashCode();
106 public sealed class DecoderReplacementFallbackBuffer : DecoderFallbackBuffer
108 // Store our default string
109 private String strDefault;
110 private int fallbackCount = -1;
111 private int fallbackIndex = -1;
114 public DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback)
116 strDefault = fallback.DefaultString;
120 public override bool Fallback(byte[] bytesUnknown, int index)
122 // We expect no previous fallback in our buffer
123 // We can't call recursively but others might (note, we don't test on last char!!!)
124 if (fallbackCount >= 1)
126 ThrowLastBytesRecursive(bytesUnknown);
129 // Go ahead and get our fallback
130 if (strDefault.Length == 0)
133 fallbackCount = strDefault.Length;
139 public override char GetNextChar()
141 // We want it to get < 0 because == 0 means that the current/last character is a fallback
142 // and we need to detect recursion. We could have a flag but we already have this counter.
146 // Do we have anything left? 0 is now last fallback char, negative is nothing left
147 if (fallbackCount < 0)
150 // Need to get it out of the buffer.
151 // Make sure it didn't wrap from the fast count-- path
152 if (fallbackCount == int.MaxValue)
158 // Now make sure its in the expected range
159 Debug.Assert(fallbackIndex < strDefault.Length && fallbackIndex >= 0,
160 "Index exceeds buffer range");
162 return strDefault[fallbackIndex];
165 public override bool MovePrevious()
167 // Back up one, only if we just processed the last character (or earlier)
168 if (fallbackCount >= -1 && fallbackIndex >= 0)
175 // Return false 'cause we couldn't do it.
179 // How many characters left to output?
180 public override int Remaining
184 // Our count is 0 for 1 character left.
185 return (fallbackCount < 0) ? 0 : fallbackCount;
190 public override unsafe void Reset()
197 // This version just counts the fallback and doesn't actually copy anything.
198 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes)
199 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the
200 // array, and we might need the index, hence the byte*
202 // return our replacement string Length
203 return strDefault.Length;