@@ -67,6 +67,10 @@ public virtual void Reset()
6767 internal unsafe byte * byteStart ;
6868 internal unsafe char * charEnd ;
6969
70+ internal Encoding _encoding ;
71+ internal DecoderNLS _decoder ;
72+ private int _originalByteCount ;
73+
7074 // Internal Reset
7175 internal unsafe void InternalReset ( )
7276 {
@@ -82,6 +86,22 @@ internal unsafe void InternalInitialize(byte* byteStart, char* charEnd)
8286 this . charEnd = charEnd ;
8387 }
8488
89+ internal static DecoderFallbackBuffer CreateAndInitialize ( Encoding encoding , DecoderNLS decoder , int originalByteCount )
90+ {
91+ // The original byte count is only used for keeping track of what 'index' value needs
92+ // to be passed to the abstract Fallback method. The index value is calculated by subtracting
93+ // 'bytes.Length' (where bytes is expected to be the entire remaining input buffer)
94+ // from the 'originalByteCount' value specified here.
95+
96+ DecoderFallbackBuffer fallbackBuffer = ( decoder is null ) ? encoding . DecoderFallback . CreateFallbackBuffer ( ) : decoder . FallbackBuffer ;
97+
98+ fallbackBuffer . _encoding = encoding ;
99+ fallbackBuffer . _decoder = decoder ;
100+ fallbackBuffer . _originalByteCount = originalByteCount ;
101+
102+ return fallbackBuffer ;
103+ }
104+
85105 // Fallback the current byte by sticking it into the remaining char buffer.
86106 // This can only be called by our encodings (other have to use the public fallback methods), so
87107 // we can use our DecoderNLS here too (except we don't).
@@ -191,6 +211,90 @@ internal unsafe virtual int InternalFallback(byte[] bytes, byte* pBytes)
191211 return 0 ;
192212 }
193213
214+ internal int InternalFallbackGetCharCount ( ReadOnlySpan < byte > remainingBytes , int fallbackLength )
215+ {
216+ return ( Fallback ( remainingBytes . Slice ( 0 , fallbackLength ) . ToArray ( ) , index : _originalByteCount - remainingBytes . Length ) )
217+ ? DrainRemainingDataForGetCharCount ( )
218+ : 0 ;
219+ }
220+
221+ internal bool TryInternalFallbackGetChars ( ReadOnlySpan < byte > remainingBytes , int fallbackLength , Span < char > chars , out int charsWritten )
222+ {
223+ if ( Fallback ( remainingBytes . Slice ( 0 , fallbackLength ) . ToArray ( ) , index : _originalByteCount - remainingBytes . Length ) )
224+ {
225+ return TryDrainRemainingDataForGetChars ( chars , out charsWritten ) ;
226+ }
227+ else
228+ {
229+ // Return true because we weren't asked to write anything, so this is a "success" in the sense that
230+ // the output buffer was large enough to hold the desired 0 chars of output.
231+
232+ charsWritten = 0 ;
233+ return true ;
234+ }
235+ }
236+
237+ private Rune GetNextRune ( )
238+ {
239+ // Call GetNextChar() and try treating it as a non-surrogate character.
240+ // If that fails, call GetNextChar() again and attempt to treat the two chars
241+ // as a surrogate pair. If that still fails, throw an exception since the fallback
242+ // mechanism is giving us a bad replacement character.
243+
244+ Rune rune ;
245+ char ch = GetNextChar ( ) ;
246+ if ( ! Rune . TryCreate ( ch , out rune ) && ! Rune . TryCreate ( ch , GetNextChar ( ) , out rune ) )
247+ {
248+ throw new ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ) ;
249+ }
250+
251+ return rune ;
252+ }
253+
254+ internal int DrainRemainingDataForGetCharCount ( )
255+ {
256+ int totalCharCount = 0 ;
257+
258+ Rune thisRune ;
259+ while ( ( thisRune = GetNextRune ( ) ) . Value != 0 )
260+ {
261+ // We need to check for overflow while tallying the fallback char count.
262+
263+ totalCharCount += thisRune . Utf16SequenceLength ;
264+ if ( totalCharCount < 0 )
265+ {
266+ InternalReset ( ) ;
267+ Encoding . ThrowConversionOverflow ( ) ;
268+ }
269+ }
270+
271+ return totalCharCount ;
272+ }
273+
274+ internal bool TryDrainRemainingDataForGetChars ( Span < char > chars , out int charsWritten )
275+ {
276+ int originalCharCount = chars . Length ;
277+
278+ Rune thisRune ;
279+ while ( ( thisRune = GetNextRune ( ) ) . Value != 0 )
280+ {
281+ if ( thisRune . TryEncode ( chars , out int charsWrittenJustNow ) )
282+ {
283+ chars = chars . Slice ( charsWrittenJustNow ) ;
284+ continue ;
285+ }
286+ else
287+ {
288+ InternalReset ( ) ;
289+ charsWritten = default ;
290+ return false ;
291+ }
292+ }
293+
294+ charsWritten = originalCharCount - chars . Length ;
295+ return true ;
296+ }
297+
194298 // private helper methods
195299 internal void ThrowLastBytesRecursive ( byte [ ] bytesUnknown )
196300 {
0 commit comments