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
;
9 public sealed class DecoderReplacementFallback
: DecoderFallback
12 private readonly string _strDefault
;
14 // Construction. Default replacement fallback uses no best fit and ? replacement string
15 public DecoderReplacementFallback() : this("?")
19 public DecoderReplacementFallback(string replacement
)
21 if (replacement
== null)
22 throw new ArgumentNullException(nameof(replacement
));
24 // Make sure it doesn't have bad surrogate pairs
25 bool bFoundHigh
= false;
26 for (int i
= 0; i
< replacement
.Length
; i
++)
29 if (char.IsSurrogate(replacement
, i
))
32 if (char.IsHighSurrogate(replacement
, i
))
34 // if already had a high one, stop
36 break; // break & throw at the bFoundHIgh below
41 // Low, did we have a high?
44 // Didn't have one, make if fail when we stop
53 // If last was high we're in trouble (not surrogate so not low surrogate, so break)
58 throw new ArgumentException(SR
.Format(SR
.Argument_InvalidCharSequenceNoIndex
, nameof(replacement
)));
60 _strDefault
= replacement
;
63 public string DefaultString
=> _strDefault
;
65 public override DecoderFallbackBuffer
CreateFallbackBuffer() =>
66 new DecoderReplacementFallbackBuffer(this);
68 // Maximum number of characters that this instance of this fallback could return
69 public override int MaxCharCount
=> _strDefault
.Length
;
71 public override bool Equals(object? value) =>
72 value is DecoderReplacementFallback that
&&
73 _strDefault
== that
._strDefault
;
75 public override int GetHashCode() => _strDefault
.GetHashCode();
78 public sealed class DecoderReplacementFallbackBuffer
: DecoderFallbackBuffer
80 // Store our default string
81 private readonly string _strDefault
;
82 private int _fallbackCount
= -1;
83 private int _fallbackIndex
= -1;
86 public DecoderReplacementFallbackBuffer(DecoderReplacementFallback fallback
)
88 _strDefault
= fallback
.DefaultString
;
92 public override bool Fallback(byte[] bytesUnknown
, int index
)
94 // We expect no previous fallback in our buffer
95 // We can't call recursively but others might (note, we don't test on last char!!!)
96 if (_fallbackCount
>= 1)
98 ThrowLastBytesRecursive(bytesUnknown
);
101 // Go ahead and get our fallback
102 if (_strDefault
.Length
== 0)
105 _fallbackCount
= _strDefault
.Length
;
111 public override char GetNextChar()
113 // We want it to get < 0 because == 0 means that the current/last character is a fallback
114 // and we need to detect recursion. We could have a flag but we already have this counter.
118 // Do we have anything left? 0 is now last fallback char, negative is nothing left
119 if (_fallbackCount
< 0)
122 // Need to get it out of the buffer.
123 // Make sure it didn't wrap from the fast count-- path
124 if (_fallbackCount
== int.MaxValue
)
130 // Now make sure its in the expected range
131 Debug
.Assert(_fallbackIndex
< _strDefault
.Length
&& _fallbackIndex
>= 0,
132 "Index exceeds buffer range");
134 return _strDefault
[_fallbackIndex
];
137 public override bool MovePrevious()
139 // Back up one, only if we just processed the last character (or earlier)
140 if (_fallbackCount
>= -1 && _fallbackIndex
>= 0)
147 // Return false 'cause we couldn't do it.
151 // How many characters left to output?
152 public override int Remaining
=>
153 // Our count is 0 for 1 character left.
154 (_fallbackCount
< 0) ? 0 : _fallbackCount
;
157 public override unsafe void Reset()
164 // This version just counts the fallback and doesn't actually copy anything.
165 internal override unsafe int InternalFallback(byte[] bytes
, byte* pBytes
) =>
166 // Right now this has both bytes and bytes[], since we might have extra bytes,
167 // hence the array, and we might need the index, hence the byte*.
168 // Return our replacement string Length