< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 211
Coverable lines: 211
Total lines: 613
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 66
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Text/TranscodingStream.cs

#LineLine coverage
 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
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.IO;
 8using System.Runtime.InteropServices;
 9using System.Threading;
 10using System.Threading.Tasks;
 11
 12namespace System.Text
 13{
 14    internal sealed class TranscodingStream : Stream
 15    {
 16        private const int DefaultReadByteBufferSize = 4 * 1024; // lifted from StreamReader.cs (FileStream)
 17
 18        // We optimistically assume 1 byte ~ 1 char during transcoding. This is a good rule of thumb
 19        // but isn't always appropriate: transcoding between single-byte and multi-byte encodings
 20        // will violate this, as will any invalid data fixups performed by the transcoder itself.
 21        // To account for these unknowns we have a minimum scratch buffer size we use during the
 22        // transcoding process. This should be generous enough to account for even the largest
 23        // fallback mechanism we're likely to see in the real world.
 24
 25        private const int MinWriteRentedArraySize = 4 * 1024;
 26        private const int MaxWriteRentedArraySize = 1024 * 1024;
 27
 28        private readonly Encoding _innerEncoding;
 29        private readonly Encoding _thisEncoding;
 30        private Stream _innerStream; // null if the wrapper has been disposed
 31        private readonly bool _leaveOpen;
 32
 33        /*
 34         * Fields used for writing bytes [this] -> chars -> bytes [inner]
 35         * Lazily initialized the first time we need to write
 36         */
 37
 38        private Encoder? _innerEncoder;
 39        private Decoder? _thisDecoder;
 40
 41        /*
 42         * Fields used for reading bytes [inner] -> chars -> bytes [this]
 43         * Lazily initialized the first time we need to read
 44         */
 45
 46        private Encoder? _thisEncoder;
 47        private Decoder? _innerDecoder;
 48        private int _readCharBufferMaxSize; // the maximum number of characters _innerDecoder.ReadChars can return
 49        private byte[]? _readBuffer; // contains the data that Read() should return
 50        private int _readBufferOffset;
 51        private int _readBufferCount;
 52
 053        internal TranscodingStream(Stream innerStream, Encoding innerEncoding, Encoding thisEncoding, bool leaveOpen)
 54        {
 055            Debug.Assert(innerStream != null);
 056            Debug.Assert(innerEncoding != null);
 057            Debug.Assert(thisEncoding != null);
 58
 059            _innerStream = innerStream;
 060            _leaveOpen = leaveOpen;
 61
 062            _innerEncoding = innerEncoding;
 063            _thisEncoding = thisEncoding;
 064        }
 65
 66        /*
 67         * Most CanXyz methods delegate to the inner stream, returning false
 68         * if this instance has been disposed. CanSeek is always false.
 69         */
 70
 071        public override bool CanRead => _innerStream?.CanRead ?? false;
 72
 073        public override bool CanSeek => false;
 74
 075        public override bool CanWrite => _innerStream?.CanWrite ?? false;
 76
 077        public override long Length => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
 78
 79        public override long Position
 80        {
 081            get => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
 082            set => ThrowHelper.ThrowNotSupportedException_UnseekableStream();
 83        }
 84
 85        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? st
 086            => TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), callback, state);
 87
 88        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? s
 089            => TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
 90
 91        protected override void Dispose(bool disposing)
 92        {
 093            Debug.Assert(disposing, "This type isn't finalizable.");
 94
 095            if (_innerStream is null)
 96            {
 097                return; // dispose called multiple times, ignore
 98            }
 99
 100            // First, flush any pending data to the inner stream.
 101
 0102            ArraySegment<byte> pendingData = FinalFlushWriteBuffers();
 0103            if (pendingData.Count != 0)
 104            {
 0105                _innerStream.Write(pendingData);
 106            }
 107
 108            // Mark our object as disposed
 109
 0110            Stream innerStream = _innerStream;
 0111            _innerStream = null!;
 112
 113            // And dispose the inner stream if needed
 114
 0115            if (!_leaveOpen)
 116            {
 0117                innerStream.Dispose();
 118            }
 0119        }
 120
 121        public override ValueTask DisposeAsync()
 122        {
 0123            if (_innerStream is null)
 124            {
 0125                return default; // dispose called multiple times, ignore
 126            }
 127
 128            // First, get any pending data destined for the inner stream.
 129
 0130            ArraySegment<byte> pendingData = FinalFlushWriteBuffers();
 131
 0132            if (pendingData.Count == 0)
 133            {
 134                // Fast path: just dispose of the object graph.
 135                // No need to write anything to the stream first.
 136
 0137                Stream innerStream = _innerStream;
 0138                _innerStream = null!;
 139
 0140                return (_leaveOpen)
 0141                    ? default /* no work to do */
 0142                    : innerStream.DisposeAsync();
 143            }
 144
 145            // Slower path; need to perform an async write followed by an async dispose.
 146
 0147            return DisposeAsyncCore(pendingData);
 148            async ValueTask DisposeAsyncCore(ArraySegment<byte> pendingData)
 149            {
 0150                Debug.Assert(pendingData.Count != 0);
 151
 0152                Stream innerStream = _innerStream;
 0153                _innerStream = null!;
 154
 0155                await innerStream.WriteAsync(pendingData.AsMemory()).ConfigureAwait(false);
 156
 0157                if (!_leaveOpen)
 158                {
 0159                    await innerStream.DisposeAsync().ConfigureAwait(false);
 160                }
 0161            }
 162        }
 163
 164        public override int EndRead(IAsyncResult asyncResult)
 0165            => TaskToAsyncResult.End<int>(asyncResult);
 166
 167        public override void EndWrite(IAsyncResult asyncResult)
 0168            => TaskToAsyncResult.End(asyncResult);
 169
 170#pragma warning disable CS3016 // Arrays as attribute arguments is not CLS-compliant
 171#pragma warning disable CS8774 // Member must have a non-null value when exiting.
 172
 173        // Sets up the data structures that are necessary before any read operation takes place,
 174        // throwing if the object is in a state where reads are not possible.
 175        [MemberNotNull(nameof(_innerDecoder), nameof(_thisEncoder), nameof(_readBuffer))]
 176        private void EnsurePreReadConditions()
 177        {
 0178            ThrowIfDisposed();
 0179            if (_innerDecoder is null)
 180            {
 0181                InitializeReadDataStructures();
 182            }
 183
 184            void InitializeReadDataStructures()
 185            {
 0186                if (!CanRead)
 187                {
 0188                    ThrowHelper.ThrowNotSupportedException_UnreadableStream();
 189                }
 190
 0191                _innerDecoder = _innerEncoding.GetDecoder();
 0192                _thisEncoder = _thisEncoding.GetEncoder();
 0193                _readCharBufferMaxSize = _innerEncoding.GetMaxCharCount(DefaultReadByteBufferSize);
 194
 195                // Can't use ArrayPool for the below array since it's an instance field of this object.
 196                // But since we never expose the raw array contents to our callers we can get away
 197                // with skipping the array zero-init during allocation. The segment points to the
 198                // data which we haven't yet read; however, we own the entire backing array and can
 199                // re-create the segment as needed once the array is repopulated.
 200
 0201                _readBuffer = GC.AllocateUninitializedArray<byte>(_thisEncoding.GetMaxByteCount(_readCharBufferMaxSize))
 0202            }
 0203        }
 204
 205        // Sets up the data structures that are necessary before any write operation takes place,
 206        // throwing if the object is in a state where writes are not possible.
 207        [MemberNotNull(nameof(_thisDecoder), nameof(_innerEncoder))]
 208        private void EnsurePreWriteConditions()
 209        {
 0210            ThrowIfDisposed();
 0211            if (_innerEncoder is null)
 212            {
 0213                InitializeReadDataStructures();
 214            }
 215
 216            void InitializeReadDataStructures()
 217            {
 0218                if (!CanWrite)
 219                {
 0220                    ThrowHelper.ThrowNotSupportedException_UnwritableStream();
 221                }
 222
 0223                _innerEncoder = _innerEncoding.GetEncoder();
 0224                _thisDecoder = _thisEncoding.GetDecoder();
 0225            }
 0226        }
 227
 228#pragma warning restore CS8774 // Member must have a non-null value when exiting.
 229#pragma warning restore CS3016 // Arrays as attribute arguments is not CLS-compliant
 230
 231        // returns any pending data that needs to be flushed to the inner stream before disposal
 232        private ArraySegment<byte> FinalFlushWriteBuffers()
 233        {
 234            // If this stream was never used for writing, no-op.
 235
 0236            if (_thisDecoder is null || _innerEncoder is null)
 237            {
 0238                return default;
 239            }
 240
 241            // convert bytes [this] -> chars
 242            // Having leftover data in our buffers should be very rare since it should only
 243            // occur if the end of the stream contains an incomplete multi-byte sequence.
 244            // Let's not bother complicating this logic with array pool rentals or allocation-
 245            // avoiding loops.
 246
 247
 0248            char[] chars = [];
 0249            int charCount = _thisDecoder.GetCharCount([], 0, 0, flush: true);
 0250            if (charCount > 0)
 251            {
 0252                chars = new char[charCount];
 0253                charCount = _thisDecoder.GetChars([], 0, 0, chars, 0, flush: true);
 254            }
 255
 256            // convert chars -> bytes [inner]
 257            // It's possible that _innerEncoder might need to perform some end-of-text fixup
 258            // (due to flush: true), even if _thisDecoder didn't need to do so.
 259
 0260            byte[] bytes = [];
 0261            int byteCount = _innerEncoder.GetByteCount(chars, 0, charCount, flush: true);
 0262            if (byteCount > 0)
 263            {
 0264                bytes = new byte[byteCount];
 0265                byteCount = _innerEncoder.GetBytes(chars, 0, charCount, bytes, 0, flush: true);
 266            }
 267
 0268            return new ArraySegment<byte>(bytes, 0, byteCount);
 269        }
 270
 271        public override void Flush()
 272        {
 273            // Don't pass flush: true to our inner decoder + encoder here, since it could cause data
 274            // corruption if a flush occurs mid-stream. Wait until the stream is being closed.
 275
 0276            ThrowIfDisposed();
 0277            _innerStream.Flush();
 0278        }
 279
 280        public override Task FlushAsync(CancellationToken cancellationToken)
 281        {
 282            // Don't pass flush: true to our inner decoder + encoder here, since it could cause data
 283            // corruption if a flush occurs mid-stream. Wait until the stream is being closed.
 284
 0285            ThrowIfDisposed();
 0286            return _innerStream.FlushAsync(cancellationToken);
 287        }
 288
 289        public override int Read(byte[] buffer, int offset, int count)
 290        {
 0291            ValidateBufferArguments(buffer, offset, count);
 292
 0293            return Read(new Span<byte>(buffer, offset, count));
 294        }
 295
 296        public override int Read(Span<byte> buffer)
 297        {
 0298            EnsurePreReadConditions();
 299
 300            // If there's no data in our pending read buffer, we'll need to populate it from
 301            // the inner stream. We read the inner stream's bytes, decode that to chars using
 302            // the 'inner' encoding, then re-encode those chars under the 'this' encoding.
 303            // We've already calculated the worst-case expansions for the intermediate buffers,
 304            // so we use GetChars / GetBytes instead of Convert to simplify the below code
 305            // and to ensure an exception is thrown if the Encoding reported an incorrect
 306            // worst-case expansion.
 307
 0308            if (_readBufferCount == 0)
 309            {
 0310                byte[] rentedBytes = ArrayPool<byte>.Shared.Rent(DefaultReadByteBufferSize);
 0311                char[] rentedChars = ArrayPool<char>.Shared.Rent(_readCharBufferMaxSize);
 312
 313                try
 314                {
 315                    int pendingReadDataPopulatedJustNow;
 316                    bool isEofReached;
 317
 318                    do
 319                    {
 320                        // Beware: Use our constant value instead of 'rentedBytes.Length' for the count
 321                        // parameter below. The reason for this is that the array pool could've returned
 322                        // a larger-than-expected array, but our worst-case expansion calculations
 323                        // performed earlier didn't take that into account.
 324
 0325                        int innerBytesReadJustNow = _innerStream.Read(rentedBytes, 0, DefaultReadByteBufferSize);
 0326                        isEofReached = (innerBytesReadJustNow == 0);
 327
 328                        // Convert bytes [inner] -> chars, then convert chars -> bytes [this].
 329                        // We can't return 0 to our caller until inner stream EOF has been reached. But if the
 330                        // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway
 331                        // since it can't yet make forward progress on the input data. If this happens, we'll
 332                        // loop so that we don't return 0 to our caller until we truly see inner stream EOF.
 333
 0334                        int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rentedCh
 0335                        pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow, _re
 0336                    } while (!isEofReached && pendingReadDataPopulatedJustNow == 0);
 337
 0338                    _readBufferOffset = 0;
 0339                    _readBufferCount = pendingReadDataPopulatedJustNow;
 0340                }
 341                finally
 342                {
 0343                    ArrayPool<byte>.Shared.Return(rentedBytes);
 0344                    ArrayPool<char>.Shared.Return(rentedChars);
 0345                }
 346            }
 347
 348            // At this point: (a) we've populated our pending read buffer and there's
 349            // useful data to return to our caller; or (b) the pending read buffer is
 350            // empty because the inner stream has reached EOF and all pending read data
 351            // has already been flushed, and we should return 0.
 352
 0353            int bytesToReturn = Math.Min(_readBufferCount, buffer.Length);
 0354            _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer);
 0355            _readBufferOffset += bytesToReturn;
 0356            _readBufferCount -= bytesToReturn;
 0357            return bytesToReturn;
 358        }
 359
 360        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 361        {
 0362            ValidateBufferArguments(buffer, offset, count);
 363
 0364            return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
 365        }
 366
 367        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken)
 368        {
 0369            EnsurePreReadConditions();
 370
 0371            if (cancellationToken.IsCancellationRequested)
 372            {
 0373                return ValueTask.FromCanceled<int>(cancellationToken);
 374            }
 375
 0376            return ReadAsyncCore(buffer, cancellationToken);
 377            async ValueTask<int> ReadAsyncCore(Memory<byte> buffer, CancellationToken cancellationToken)
 378            {
 379                // If there's no data in our pending read buffer, we'll need to populate it from
 380                // the inner stream. We read the inner stream's bytes, decode that to chars using
 381                // the 'inner' encoding, then re-encode those chars under the 'this' encoding.
 382                // We've already calculated the worst-case expansions for the intermediate buffers,
 383                // so we use GetChars / GetBytes instead of Convert to simplify the below code
 384                // and to ensure an exception is thrown if the Encoding reported an incorrect
 385                // worst-case expansion.
 386
 0387                if (_readBufferCount == 0)
 388                {
 0389                    byte[] rentedBytes = ArrayPool<byte>.Shared.Rent(DefaultReadByteBufferSize);
 0390                    char[] rentedChars = ArrayPool<char>.Shared.Rent(_readCharBufferMaxSize);
 391
 392                    try
 393                    {
 394                        int pendingReadDataPopulatedJustNow;
 395                        bool isEofReached;
 396
 397                        do
 398                        {
 399                            // Beware: Use our constant value instead of 'rentedBytes.Length' when creating
 400                            // the Mem<byte> struct. The reason for this is that the array pool could've returned
 401                            // a larger-than-expected array, but our worst-case expansion calculations
 402                            // performed earlier didn't take that into account.
 403
 0404                            int innerBytesReadJustNow = await _innerStream.ReadAsync(rentedBytes.AsMemory(0, DefaultRead
 0405                            isEofReached = (innerBytesReadJustNow == 0);
 406
 407                            // Convert bytes [inner] -> chars, then convert chars -> bytes [this].
 408                            // We can't return 0 to our caller until inner stream EOF has been reached. But if the
 409                            // inner stream returns a non-empty but incomplete buffer, GetBytes may return 0 anyway
 410                            // since it can't yet make forward progress on the input data. If this happens, we'll
 411                            // loop so that we don't return 0 to our caller until we truly see inner stream EOF.
 412
 0413                            int charsDecodedJustNow = _innerDecoder.GetChars(rentedBytes, 0, innerBytesReadJustNow, rent
 0414                            pendingReadDataPopulatedJustNow = _thisEncoder.GetBytes(rentedChars, 0, charsDecodedJustNow,
 0415                        } while (!isEofReached && pendingReadDataPopulatedJustNow == 0);
 416
 0417                        _readBufferOffset = 0;
 0418                        _readBufferCount = pendingReadDataPopulatedJustNow;
 0419                    }
 420                    finally
 421                    {
 0422                        ArrayPool<byte>.Shared.Return(rentedBytes);
 0423                        ArrayPool<char>.Shared.Return(rentedChars);
 0424                    }
 425                }
 426
 427                // At this point: (a) we've populated our pending read buffer and there's
 428                // useful data to return to our caller; or (b) the pending read buffer is
 429                // empty because the inner stream has reached EOF and all pending read data
 430                // has already been flushed, and we should return 0.
 431
 0432                int bytesToReturn = Math.Min(_readBufferCount, buffer.Length);
 0433                _readBuffer.AsSpan(_readBufferOffset, bytesToReturn).CopyTo(buffer.Span);
 0434                _readBufferOffset += bytesToReturn;
 0435                _readBufferCount -= bytesToReturn;
 0436                return bytesToReturn;
 437            }
 438        }
 439
 440        public override int ReadByte()
 441        {
 0442            byte b = 0;
 0443            return Read(new Span<byte>(ref b)) != 0 ? b : -1;
 444        }
 445
 446        public override long Seek(long offset, SeekOrigin origin)
 0447            => throw new NotSupportedException(SR.NotSupported_UnseekableStream);
 448
 449        public override void SetLength(long value)
 0450            => ThrowHelper.ThrowNotSupportedException_UnseekableStream();
 451
 452        [StackTraceHidden]
 453        private void ThrowIfDisposed()
 454        {
 0455            if (_innerStream is null)
 456            {
 0457                ThrowObjectDisposedException();
 458            }
 0459        }
 460
 461        [DoesNotReturn]
 462        [StackTraceHidden]
 463        private void ThrowObjectDisposedException()
 464        {
 0465            ThrowHelper.ThrowObjectDisposedException_StreamClosed(GetType().Name);
 466        }
 467
 468        public override void Write(byte[] buffer, int offset, int count)
 469        {
 0470            ValidateBufferArguments(buffer, offset, count);
 471
 0472            Write(new ReadOnlySpan<byte>(buffer, offset, count));
 0473        }
 474
 475        public override void Write(ReadOnlySpan<byte> buffer)
 476        {
 0477            EnsurePreWriteConditions();
 478
 0479            if (buffer.IsEmpty)
 480            {
 0481                return;
 482            }
 483
 0484            int rentalLength = Math.Clamp(buffer.Length, MinWriteRentedArraySize, MaxWriteRentedArraySize);
 485
 0486            char[] scratchChars = ArrayPool<char>.Shared.Rent(rentalLength);
 0487            byte[] scratchBytes = ArrayPool<byte>.Shared.Rent(rentalLength);
 488
 489            try
 490            {
 491                bool decoderFinished, encoderFinished;
 492                do
 493                {
 494                    // convert bytes [this] -> chars
 495
 0496                    _thisDecoder.Convert(
 0497                        bytes: buffer,
 0498                        chars: scratchChars,
 0499                        flush: false,
 0500                        out int bytesConsumed,
 0501                        out int charsWritten,
 0502                        out decoderFinished);
 503
 0504                    buffer = buffer.Slice(bytesConsumed);
 505
 506                    // convert chars -> bytes [inner]
 507
 0508                    Span<char> decodedChars = scratchChars.AsSpan(0, charsWritten);
 509
 510                    do
 511                    {
 0512                        _innerEncoder.Convert(
 0513                            chars: decodedChars,
 0514                            bytes: scratchBytes,
 0515                            flush: false,
 0516                            out int charsConsumed,
 0517                            out int bytesWritten,
 0518                            out encoderFinished);
 519
 0520                        decodedChars = decodedChars.Slice(charsConsumed);
 521
 522                        // It's more likely that the inner stream provides an optimized implementation of
 523                        // Write(byte[], ...) over Write(ROS<byte>), so we'll prefer the byte[]-based overloads.
 524
 0525                        _innerStream.Write(scratchBytes, 0, bytesWritten);
 0526                    } while (!encoderFinished);
 0527                } while (!decoderFinished);
 0528            }
 529            finally
 530            {
 0531                ArrayPool<char>.Shared.Return(scratchChars);
 0532                ArrayPool<byte>.Shared.Return(scratchBytes);
 0533            }
 0534        }
 535
 536        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 537        {
 0538            ValidateBufferArguments(buffer, offset, count);
 539
 0540            return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
 541        }
 542
 543        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
 544        {
 0545            EnsurePreWriteConditions();
 546
 0547            if (cancellationToken.IsCancellationRequested)
 548            {
 0549                return ValueTask.FromCanceled(cancellationToken);
 550            }
 551
 0552            if (buffer.IsEmpty)
 553            {
 0554                return ValueTask.CompletedTask;
 555            }
 556
 0557            return WriteAsyncCore(buffer, cancellationToken);
 558            async ValueTask WriteAsyncCore(ReadOnlyMemory<byte> remainingOuterEncodedBytes, CancellationToken cancellati
 559            {
 0560                int rentalLength = Math.Clamp(remainingOuterEncodedBytes.Length, MinWriteRentedArraySize, MaxWriteRented
 561
 0562                char[] scratchChars = ArrayPool<char>.Shared.Rent(rentalLength);
 0563                byte[] scratchBytes = ArrayPool<byte>.Shared.Rent(rentalLength);
 564
 565                try
 566                {
 567                    bool decoderFinished, encoderFinished;
 568                    do
 569                    {
 570                        // convert bytes [this] -> chars
 571
 0572                        _thisDecoder.Convert(
 0573                            bytes: remainingOuterEncodedBytes.Span,
 0574                            chars: scratchChars,
 0575                            flush: false,
 0576                            out int bytesConsumed,
 0577                            out int charsWritten,
 0578                            out decoderFinished);
 579
 0580                        remainingOuterEncodedBytes = remainingOuterEncodedBytes.Slice(bytesConsumed);
 581
 582                        // convert chars -> bytes [inner]
 583
 0584                        ArraySegment<char> decodedChars = new ArraySegment<char>(scratchChars, 0, charsWritten);
 585
 586                        do
 587                        {
 0588                            _innerEncoder.Convert(
 0589                                chars: decodedChars,
 0590                                bytes: scratchBytes,
 0591                                flush: false,
 0592                                out int charsConsumed,
 0593                                out int bytesWritten,
 0594                                out encoderFinished);
 595
 0596                            decodedChars = decodedChars.Slice(charsConsumed);
 0597                            await _innerStream.WriteAsync(new ReadOnlyMemory<byte>(scratchBytes, 0, bytesWritten), cance
 0598                        } while (!encoderFinished);
 0599                    } while (!decoderFinished);
 0600                }
 601                finally
 602                {
 0603                    ArrayPool<char>.Shared.Return(scratchChars);
 0604                    ArrayPool<byte>.Shared.Return(scratchBytes);
 0605                }
 0606            }
 607        }
 608
 609        public override void WriteByte(byte value)
 0610            => Write(new ReadOnlySpan<byte>(in value));
 611    }
 612}
 613

Methods/Properties

.ctor(System.IO.Stream,System.Text.Encoding,System.Text.Encoding,System.Boolean)
CanRead()
CanSeek()
CanWrite()
Length()
Position()
Position(System.Int64)
BeginRead(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
BeginWrite(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
Dispose(System.Boolean)
DisposeAsync()
DisposeAsyncCore(System.ArraySegment`1<System.Byte>)
EndRead(System.IAsyncResult)
EndWrite(System.IAsyncResult)
EnsurePreReadConditions()
InitializeReadDataStructures()
EnsurePreWriteConditions()
InitializeReadDataStructures()
FinalFlushWriteBuffers()
Flush()
FlushAsync(System.Threading.CancellationToken)
Read(System.Byte[],System.Int32,System.Int32)
Read(System.Span`1<System.Byte>)
ReadAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
ReadAsyncCore(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
ReadByte()
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
ThrowIfDisposed()
ThrowObjectDisposedException()
Write(System.Byte[],System.Int32,System.Int32)
Write(System.ReadOnlySpan`1<System.Byte>)
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteAsyncCore(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteByte(System.Byte)