File: src\libraries\System.Private.CoreLib\src\System\IO\FileStream.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.IO.Strategies;
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
 
namespace System.IO
{
    public class FileStream : Stream
    {
        internal const int DefaultBufferSize = 4096;
        internal const FileShare DefaultShare = FileShare.Read;
        private const bool DefaultIsAsync = false;
 
        private readonly FileStreamStrategy _strategy;
 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This constructor has been deprecated. Use FileStream(SafeFileHandle handle, FileAccess access) instead.")]
        public FileStream(IntPtr handle, FileAccess access)
            : this(handle, access, true, DefaultBufferSize, DefaultIsAsync)
        {
        }
 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This constructor has been deprecated. Use FileStream(SafeFileHandle handle, FileAccess access) and optionally make a new SafeFileHandle with ownsHandle=false if needed instead.")]
        public FileStream(IntPtr handle, FileAccess access, bool ownsHandle)
            : this(handle, access, ownsHandle, DefaultBufferSize, DefaultIsAsync)
        {
        }
 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This constructor has been deprecated. Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize) and optionally make a new SafeFileHandle with ownsHandle=false if needed instead.")]
        public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
            : this(handle, access, ownsHandle, bufferSize, DefaultIsAsync)
        {
        }
 
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This constructor has been deprecated. Use FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) and optionally make a new SafeFileHandle with ownsHandle=false if needed instead.")]
        public FileStream(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
        {
            SafeFileHandle safeHandle = new SafeFileHandle(handle, ownsHandle: ownsHandle);
            try
            {
                ValidateHandle(safeHandle, access, bufferSize, isAsync);
 
                _strategy = FileStreamHelpers.ChooseStrategy(this, safeHandle, access, bufferSize, isAsync);
            }
            catch
            {
                // We don't want to take ownership of closing passed in handles
                // *unless* the constructor completes successfully.
                GC.SuppressFinalize(safeHandle);
 
                // This would also prevent Close from being called, but is unnecessary
                // as we've removed the object from the finalizer queue.
                //
                // safeHandle.SetHandleAsInvalid();
                throw;
            }
        }
 
        private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize)
        {
            if (handle.IsInvalid)
            {
                throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
            }
            else if (access < FileAccess.Read || access > FileAccess.ReadWrite)
            {
                throw new ArgumentOutOfRangeException(nameof(access), SR.ArgumentOutOfRange_Enum);
            }
            else if (bufferSize < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(nameof(bufferSize));
            }
            else if (handle.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
        }
 
        private static void ValidateHandle(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
        {
            ValidateHandle(handle, access, bufferSize);
 
            if (isAsync && !handle.IsAsync)
            {
                ThrowHelper.ThrowArgumentException_HandleNotAsync(nameof(handle));
            }
            else if (!isAsync && handle.IsAsync)
            {
                ThrowHelper.ThrowArgumentException_HandleNotSync(nameof(handle));
            }
        }
 
        public FileStream(SafeFileHandle handle, FileAccess access)
            : this(handle, access, DefaultBufferSize)
        {
        }
 
        public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize)
        {
            ValidateHandle(handle, access, bufferSize);
 
            _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, handle.IsAsync);
        }
 
        public FileStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
        {
            ValidateHandle(handle, access, bufferSize, isAsync);
 
            _strategy = FileStreamHelpers.ChooseStrategy(this, handle, access, bufferSize, isAsync);
        }
 
        public FileStream(string path, FileMode mode)
            : this(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultIsAsync)
        {
        }
 
        public FileStream(string path, FileMode mode, FileAccess access)
            : this(path, mode, access, DefaultShare, DefaultBufferSize, DefaultIsAsync)
        {
        }
 
        public FileStream(string path, FileMode mode, FileAccess access, FileShare share)
            : this(path, mode, access, share, DefaultBufferSize, DefaultIsAsync)
        {
        }
 
        public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
            : this(path, mode, access, share, bufferSize, DefaultIsAsync)
        {
        }
 
        public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
            : this(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None)
        {
        }
 
        public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
            : this(path, mode, access, share, bufferSize, options, 0)
        {
        }
 
        ~FileStream()
        {
            // Preserved for compatibility since FileStream has defined a
            // finalizer in past releases and derived classes may depend
            // on Dispose(false) call.
            Dispose(false);
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="FileStream" /> class with the specified path, creation mode, read/write and sharing permission, the access other FileStreams can have to the same file, the buffer size,  additional file options and the allocation size.
        /// </summary>
        /// <param name="path">A relative or absolute path for the file that the current <see cref="FileStream" /> instance will encapsulate.</param>
        /// <param name="options">An object that describes optional <see cref="FileStream" /> parameters to use.</param>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="path" /> or <paramref name="options" /> is <see langword="null" />.</exception>
        /// <exception cref="T:System.ArgumentException"><paramref name="path" /> is an empty string (""), contains only white space, or contains one or more invalid characters.
        /// -or-
        /// <paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in an NTFS environment.</exception>
        /// <exception cref="T:System.NotSupportedException"><paramref name="path" /> refers to a non-file device, such as <c>CON:</c>, <c>COM1:</c>, <c>LPT1:</c>, etc. in a non-NTFS environment.</exception>
        /// <exception cref="T:System.IO.FileNotFoundException">The file cannot be found, such as when <see cref="FileStreamOptions.Mode" /> is <see langword="FileMode.Truncate" /> or <see langword="FileMode.Open" />, and the file specified by <paramref name="path" /> does not exist. The file must already exist in these modes.</exception>
        /// <exception cref="T:System.IO.IOException">An I/O error, such as specifying <see langword="FileMode.CreateNew" /> when the file specified by <paramref name="path" /> already exists, occurred.
        ///  -or-
        ///  The stream has been closed.
        ///  -or-
        ///  The disk was full (when <see cref="FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).
        ///  -or-
        ///  The file was too large (when <see cref="FileStreamOptions.PreallocationSize" /> was provided and <paramref name="path" /> was pointing to a regular file).</exception>
        /// <exception cref="T:System.Security.SecurityException">The caller does not have the required permission.</exception>
        /// <exception cref="T:System.IO.DirectoryNotFoundException">The specified path is invalid, such as being on an unmapped drive.</exception>
        /// <exception cref="T:System.UnauthorizedAccessException">The <see cref="FileStreamOptions.Access" /> requested is not permitted by the operating system for the specified <paramref name="path" />, such as when <see cref="FileStreamOptions.Access" />  is <see cref="FileAccess.Write" /> or <see cref="FileAccess.ReadWrite" /> and the file or directory is set for read-only access.
        ///  -or-
        /// <see cref="F:System.IO.FileOptions.Encrypted" /> is specified for <see cref="FileStreamOptions.Options" /> , but file encryption is not supported on the current platform.</exception>
        /// <exception cref="T:System.IO.PathTooLongException">The specified path, file name, or both exceed the system-defined maximum length. </exception>
        public FileStream(string path, FileStreamOptions options)
        {
            ArgumentException.ThrowIfNullOrEmpty(path);
            ArgumentNullException.ThrowIfNull(options);
            if ((options.Access & FileAccess.Read) != 0 && options.Mode == FileMode.Append)
            {
                throw new ArgumentException(SR.Argument_InvalidAppendMode, nameof(options));
            }
            else if ((options.Access & FileAccess.Write) == 0)
            {
                if (options.Mode == FileMode.Truncate || options.Mode == FileMode.CreateNew || options.Mode == FileMode.Create || options.Mode == FileMode.Append)
                {
                    throw new ArgumentException(SR.Format(SR.Argument_InvalidFileModeAndAccessCombo, options.Mode, options.Access), nameof(options));
                }
            }
 
            if (options.PreallocationSize > 0)
            {
                FileStreamHelpers.ValidateArgumentsForPreallocation(options.Mode, options.Access);
            }
 
            if (options.UnixCreateMode.HasValue)
            {
                // Only allow UnixCreateMode for file modes that can create a new file.
                if (options.Mode == FileMode.Truncate || options.Mode == FileMode.Open)
                {
                    throw new ArgumentException(SR.Argument_InvalidUnixCreateMode, nameof(options));
                }
            }
 
            FileStreamHelpers.SerializationGuard(options.Access);
 
            _strategy = FileStreamHelpers.ChooseStrategy(
                this, path, options.Mode, options.Access, options.Share, options.BufferSize, options.Options, options.PreallocationSize, options.UnixCreateMode);
        }
 
        private FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options, long preallocationSize)
        {
            FileStreamHelpers.ValidateArguments(path, mode, access, share, bufferSize, options, preallocationSize);
 
            _strategy = FileStreamHelpers.ChooseStrategy(this, path, mode, access, share, bufferSize, options, preallocationSize, unixCreateMode: null);
        }
 
        [Obsolete("FileStream.Handle has been deprecated. Use FileStream's SafeFileHandle property instead.")]
        public virtual IntPtr Handle => _strategy.Handle;
 
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("macos")]
        [UnsupportedOSPlatform("tvos")]
        [UnsupportedOSPlatform("freebsd")]
        public virtual void Lock(long position, long length)
        {
            if (position < 0 || length < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(position < 0 ? nameof(position) : nameof(length));
            }
            else if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
 
            _strategy.Lock(position, length);
        }
 
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("macos")]
        [UnsupportedOSPlatform("tvos")]
        [UnsupportedOSPlatform("freebsd")]
        public virtual void Unlock(long position, long length)
        {
            if (position < 0 || length < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException_NeedNonNegNum(position < 0 ? nameof(position) : nameof(length));
            }
            else if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
 
            _strategy.Unlock(position, length);
        }
 
        public override Task FlushAsync(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return Task.FromCanceled(cancellationToken);
            }
            else if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
 
            return _strategy.FlushAsync(cancellationToken);
        }
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            ValidateReadWriteArgs(buffer, offset, count);
 
            return _strategy.Read(buffer, offset, count);
        }
 
        public override int Read(Span<byte> buffer) => _strategy.Read(buffer);
 
        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            ValidateBufferArguments(buffer, offset, count);
 
            if (cancellationToken.IsCancellationRequested)
            {
                return Task.FromCanceled<int>(cancellationToken);
            }
            else if (!_strategy.CanRead)
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
 
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }
 
            return _strategy.ReadAsync(buffer, offset, count, cancellationToken);
        }
 
        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return ValueTask.FromCanceled<int>(cancellationToken);
            }
            else if (!_strategy.CanRead)
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
 
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }
 
            return _strategy.ReadAsync(buffer, cancellationToken);
        }
 
        public override void Write(byte[] buffer, int offset, int count)
        {
            ValidateReadWriteArgs(buffer, offset, count);
 
            _strategy.Write(buffer, offset, count);
        }
 
        public override void Write(ReadOnlySpan<byte> buffer) => _strategy.Write(buffer);
 
        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
        {
            ValidateBufferArguments(buffer, offset, count);
 
            if (cancellationToken.IsCancellationRequested)
            {
                return Task.FromCanceled(cancellationToken);
            }
            else if (!_strategy.CanWrite)
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
 
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }
 
            return _strategy.WriteAsync(buffer, offset, count, cancellationToken);
        }
 
        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return ValueTask.FromCanceled(cancellationToken);
            }
            else if (!_strategy.CanWrite)
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
 
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }
 
            return _strategy.WriteAsync(buffer, cancellationToken);
        }
 
        /// <summary>
        /// Clears buffers for this stream and causes any buffered data to be written to the file.
        /// </summary>
        public override void Flush()
        {
            // Make sure that we call through the public virtual API
            Flush(flushToDisk: false);
        }
 
        /// <summary>
        /// Clears buffers for this stream, and if <param name="flushToDisk"/> is true,
        /// causes any buffered data to be written to the file.
        /// </summary>
        public virtual void Flush(bool flushToDisk)
        {
            if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
 
            _strategy.Flush(flushToDisk);
        }
 
        /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
        public override bool CanRead => _strategy.CanRead;
 
        /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
        public override bool CanWrite => _strategy.CanWrite;
 
        /// <summary>Validates arguments to Read and Write and throws resulting exceptions.</summary>
        /// <param name="buffer">The buffer to read from or write to.</param>
        /// <param name="offset">The zero-based offset into the buffer.</param>
        /// <param name="count">The maximum number of bytes to read or write.</param>
        private void ValidateReadWriteArgs(byte[] buffer, int offset, int count)
        {
            ValidateBufferArguments(buffer, offset, count);
            if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
        }
 
        /// <summary>Sets the length of this stream to the given value.</summary>
        /// <param name="value">The new length of the stream.</param>
        public override void SetLength(long value)
        {
            if (value < 0)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }
            else if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
            else if (!CanSeek)
            {
                ThrowHelper.ThrowNotSupportedException_UnseekableStream();
            }
            else if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }
 
            _strategy.SetLength(value);
        }
 
        public virtual SafeFileHandle SafeFileHandle => _strategy.SafeFileHandle;
 
        /// <summary>Gets the path that was passed to the constructor.</summary>
        public virtual string Name => _strategy.Name;
 
        /// <summary>Gets a value indicating whether the stream was opened for I/O to be performed synchronously or asynchronously.</summary>
        public virtual bool IsAsync => _strategy.IsAsync;
 
        /// <summary>Gets the length of the stream in bytes.</summary>
        public override long Length
        {
            get
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
                else if (!CanSeek)
                {
                    ThrowHelper.ThrowNotSupportedException_UnseekableStream();
                }
 
                return _strategy.Length;
            }
        }
 
        /// <summary>Gets or sets the position within the current stream</summary>
        public override long Position
        {
            get
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
                else if (!CanSeek)
                {
                    ThrowHelper.ThrowNotSupportedException_UnseekableStream();
                }
 
                return _strategy.Position;
            }
            set
            {
                if (value < 0)
                {
                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
                }
                else if (!CanSeek)
                {
                    if (_strategy.IsClosed)
                    {
                        ThrowHelper.ThrowObjectDisposedException_FileClosed();
                    }
 
                    ThrowHelper.ThrowNotSupportedException_UnseekableStream();
                }
 
                _strategy.Position = value;
            }
        }
 
        /// <summary>
        /// Reads a byte from the file stream.  Returns the byte cast to an int
        /// or -1 if reading from the end of the stream.
        /// </summary>
        public override int ReadByte() => _strategy.ReadByte();
 
        /// <summary>
        /// Writes a byte to the current position in the stream and advances the position
        /// within the stream by one byte.
        /// </summary>
        /// <param name="value">The byte to write to the stream.</param>
        public override void WriteByte(byte value) => _strategy.WriteByte(value);
 
        // _strategy can be null only when ctor has thrown
        protected override void Dispose(bool disposing) => _strategy?.DisposeInternal(disposing);
 
        public override async ValueTask DisposeAsync()
        {
            await _strategy.DisposeAsync().ConfigureAwait(false);
 
            // For compatibility, derived classes must only call base.DisposeAsync(),
            // otherwise we would end up calling Dispose twice (one from base.DisposeAsync() and one from here).
            if (!_strategy.IsDerived)
            {
                Dispose(false);
                GC.SuppressFinalize(this);
            }
        }
 
        public override void CopyTo(Stream destination, int bufferSize)
        {
            ValidateCopyToArguments(destination, bufferSize);
            _strategy.CopyTo(destination, bufferSize);
        }
 
        /// <inheritdoc />
        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
        {
            ValidateCopyToArguments(destination, bufferSize);
            return _strategy.CopyToAsync(destination, bufferSize, cancellationToken);
        }
 
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
        {
            ValidateBufferArguments(buffer, offset, count);
 
            if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
            else if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }
 
            return _strategy.BeginRead(buffer, offset, count, callback, state);
        }
 
        public override int EndRead(IAsyncResult asyncResult)
        {
            ArgumentNullException.ThrowIfNull(asyncResult);
 
            return _strategy.EndRead(asyncResult);
        }
 
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
        {
            ValidateBufferArguments(buffer, offset, count);
 
            if (_strategy.IsClosed)
            {
                ThrowHelper.ThrowObjectDisposedException_FileClosed();
            }
            else if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }
 
            return _strategy.BeginWrite(buffer, offset, count, callback, state);
        }
 
        public override void EndWrite(IAsyncResult asyncResult)
        {
            ArgumentNullException.ThrowIfNull(asyncResult);
 
            _strategy.EndWrite(asyncResult);
        }
 
        public override bool CanSeek => _strategy.CanSeek;
 
        public override long Seek(long offset, SeekOrigin origin)
        {
            if (origin < SeekOrigin.Begin || origin > SeekOrigin.End)
            {
                throw new ArgumentException(SR.Argument_InvalidSeekOrigin, nameof(origin));
            }
            else if (!CanSeek)
            {
                if (_strategy.IsClosed)
                {
                    ThrowHelper.ThrowObjectDisposedException_FileClosed();
                }
 
                ThrowHelper.ThrowNotSupportedException_UnseekableStream();
            }
 
            return _strategy.Seek(offset, origin);
        }
 
        internal Task BaseFlushAsync(CancellationToken cancellationToken)
            => base.FlushAsync(cancellationToken);
 
        internal int BaseRead(Span<byte> buffer) => base.Read(buffer);
 
        internal Task<int> BaseReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            => base.ReadAsync(buffer, offset, count, cancellationToken);
 
        internal ValueTask<int> BaseReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
            => base.ReadAsync(buffer, cancellationToken);
 
        internal void BaseWrite(ReadOnlySpan<byte> buffer) => base.Write(buffer);
 
        internal Task BaseWriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
            => base.WriteAsync(buffer, offset, count, cancellationToken);
 
        internal ValueTask BaseWriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
            => base.WriteAsync(buffer, cancellationToken);
 
        internal ValueTask BaseDisposeAsync()
            => base.DisposeAsync();
 
        internal Task BaseCopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
            => base.CopyToAsync(destination, bufferSize, cancellationToken);
 
        internal IAsyncResult BaseBeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
            => base.BeginRead(buffer, offset, count, callback, state);
 
        internal int BaseEndRead(IAsyncResult asyncResult) => base.EndRead(asyncResult);
 
        internal IAsyncResult BaseBeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
            => base.BeginWrite(buffer, offset, count, callback, state);
 
        internal void BaseEndWrite(IAsyncResult asyncResult) => base.EndWrite(asyncResult);
    }
}