// // DuplexStream.cs // // Author: Jeffrey Stedfast // // Copyright (c) 2013-2020 .NET Foundation and Contributors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MailKit { /// /// A duplex stream. /// class DuplexStream : Stream { bool disposed; /// /// Initializes a new instance of the class. /// /// The stream to use for input. /// The stream to use for output. /// /// is null. /// -or- /// is null. /// public DuplexStream (Stream istream, Stream ostream) { if (istream == null) throw new ArgumentNullException (nameof (istream)); if (ostream == null) throw new ArgumentNullException (nameof (ostream)); InputStream = istream; OutputStream = ostream; } /// /// Gets the input stream. /// /// The input stream. public Stream InputStream { get; private set; } /// /// Gets the output stream. /// /// The output stream. public Stream OutputStream { get; private set; } /// /// Gets whether the stream supports reading. /// /// true if the stream supports reading; otherwise, false. public override bool CanRead { get { return true; } } /// /// Gets whether the stream supports writing. /// /// true if the stream supports writing; otherwise, false. public override bool CanWrite { get { return true; } } /// /// Gets whether the stream supports seeking. /// /// true if the stream supports seeking; otherwise, false. public override bool CanSeek { get { return false; } } /// /// Gets whether the stream supports I/O timeouts. /// /// true if the stream supports I/O timeouts; otherwise, false. public override bool CanTimeout { get { return InputStream.CanTimeout && OutputStream.CanTimeout; } } /// /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to read before timing out. /// /// A value, in miliseconds, that determines how long the stream will attempt to read before timing out. /// The read timeout. public override int ReadTimeout { get { return InputStream.ReadTimeout; } set { InputStream.ReadTimeout = value; } } /// /// Gets or sets a value, in miliseconds, that determines how long the stream will attempt to write before timing out. /// /// A value, in miliseconds, that determines how long the stream will attempt to write before timing out. /// The write timeout. public override int WriteTimeout { get { return OutputStream.WriteTimeout; } set { OutputStream.WriteTimeout = value; } } /// /// Gets or sets the position within the current stream. /// /// The current position within the stream. /// The position of the stream. /// /// The stream does not support seeking. /// public override long Position { get { throw new NotSupportedException (); } set { throw new NotSupportedException (); } } /// /// Gets the length in bytes of the stream. /// /// A long value representing the length of the stream in bytes. /// The length of the stream. /// /// The stream does not support seeking. /// public override long Length { get { throw new NotSupportedException (); } } static void ValidateArguments (byte[] buffer, int offset, int count) { if (buffer == null) throw new ArgumentNullException (nameof (buffer)); if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException (nameof (offset)); if (count < 0 || count > (buffer.Length - offset)) throw new ArgumentOutOfRangeException (nameof (count)); } void CheckDisposed () { if (disposed) throw new ObjectDisposedException (nameof (DuplexStream)); } /// /// Reads a sequence of bytes from the stream and advances the position /// within the stream by the number of bytes read. /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many /// bytes are not currently available, or zero (0) if the end of the stream has been reached. /// The buffer. /// The buffer offset. /// The number of bytes to read. /// /// is null. /// /// /// is less than zero or greater than the length of . /// -or- /// The is not large enough to contain bytes strting /// at the specified . /// /// /// The stream has been disposed. /// /// /// An I/O error occurred. /// public override int Read (byte[] buffer, int offset, int count) { CheckDisposed (); ValidateArguments (buffer, offset, count); return InputStream.Read (buffer, offset, count); } /// /// Reads a sequence of bytes from the stream and advances the position /// within the stream by the number of bytes read. /// /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many /// bytes are not currently available, or zero (0) if the end of the stream has been reached. /// The buffer. /// The buffer offset. /// The number of bytes to read. /// The cancellation token. /// /// is null. /// /// /// is less than zero or greater than the length of . /// -or- /// The is not large enough to contain bytes strting /// at the specified . /// /// /// The stream has been disposed. /// /// /// An I/O error occurred. /// public override Task ReadAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) { CheckDisposed (); ValidateArguments (buffer, offset, count); return InputStream.ReadAsync (buffer, offset, count, cancellationToken); } /// /// Writes a sequence of bytes to the stream and advances the current /// position within this stream by the number of bytes written. /// /// The buffer to write. /// The offset of the first byte to write. /// The number of bytes to write. /// /// is null. /// /// /// is less than zero or greater than the length of . /// -or- /// The is not large enough to contain bytes strting /// at the specified . /// /// /// The stream has been disposed. /// /// /// The stream does not support writing. /// /// /// An I/O error occurred. /// public override void Write (byte[] buffer, int offset, int count) { CheckDisposed (); ValidateArguments (buffer, offset, count); OutputStream.Write (buffer, offset, count); } /// /// Writes a sequence of bytes to the stream and advances the current /// position within this stream by the number of bytes written. /// /// A task that represents the asynchronous write operation. /// The buffer to write. /// The offset of the first byte to write. /// The number of bytes to write. /// The cancellation token. /// /// is null. /// /// /// is less than zero or greater than the length of . /// -or- /// The is not large enough to contain bytes strting /// at the specified . /// /// /// The stream has been disposed. /// /// /// The stream does not support writing. /// /// /// An I/O error occurred. /// public override Task WriteAsync (byte[] buffer, int offset, int count, CancellationToken cancellationToken) { CheckDisposed (); ValidateArguments (buffer, offset, count); return OutputStream.WriteAsync (buffer, offset, count, cancellationToken); } /// /// Clears all output buffers for this stream and causes any buffered data to be written /// to the underlying device. /// /// /// The stream has been disposed. /// /// /// The stream does not support writing. /// /// /// An I/O error occurred. /// public override void Flush () { CheckDisposed (); OutputStream.Flush (); } /// /// Clears all output buffers for this stream and causes any buffered data to be written /// to the underlying device. /// /// A task that represents the asynchronous flush operation. /// The cancellation token. /// /// The stream has been disposed. /// /// /// The stream does not support writing. /// /// /// An I/O error occurred. /// public override Task FlushAsync (CancellationToken cancellationToken) { CheckDisposed (); return OutputStream.FlushAsync (cancellationToken); } /// /// Sets the position within the current stream. /// /// The new position within the stream. /// The offset into the stream relative to the . /// The origin to seek from. /// /// The stream does not support seeking. /// public override long Seek (long offset, SeekOrigin origin) { throw new NotSupportedException (); } /// /// Sets the length of the stream. /// /// The desired length of the stream in bytes. /// /// The stream does not support setting the length. /// public override void SetLength (long value) { throw new NotSupportedException (); } /// /// Releases the unmanaged resources used by the and /// optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; /// false to release only the unmanaged resources. protected override void Dispose (bool disposing) { if (disposing && !disposed) { OutputStream.Dispose (); InputStream.Dispose (); disposed = true; } base.Dispose (disposing); } } }