// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.Dtf.Compression { using System; using System.IO; /// /// Duplicates a source stream by maintaining a separate position. /// /// /// WARNING: duplicate streams are not thread-safe with respect to each other or the original stream. /// If multiple threads use duplicate copies of the same stream, they must synchronize for any operations. /// public class DuplicateStream : Stream { private Stream source; private long position; /// /// Creates a new duplicate of a stream. /// /// source of the duplicate public DuplicateStream(Stream source) { if (source == null) { throw new ArgumentNullException("source"); } this.source = DuplicateStream.OriginalStream(source); } /// /// Gets the original stream that was used to create the duplicate. /// public Stream Source { get { return this.source; } } /// /// Gets a value indicating whether the source stream supports reading. /// /// true if the stream supports reading; otherwise, false. public override bool CanRead { get { return this.source.CanRead; } } /// /// Gets a value indicating whether the source stream supports writing. /// /// true if the stream supports writing; otherwise, false. public override bool CanWrite { get { return this.source.CanWrite; } } /// /// Gets a value indicating whether the source stream supports seeking. /// /// true if the stream supports seeking; otherwise, false. public override bool CanSeek { get { return this.source.CanSeek; } } /// /// Gets the length of the source stream. /// public override long Length { get { return this.source.Length; } } /// /// Gets or sets the position of the current stream, /// ignoring the position of the source stream. /// public override long Position { get { return this.position; } set { this.position = value; } } /// /// Retrieves the original stream from a possible duplicate stream. /// /// Possible duplicate stream. /// If the stream is a DuplicateStream, returns /// the duplicate's source; otherwise returns the same stream. public static Stream OriginalStream(Stream stream) { DuplicateStream dupStream = stream as DuplicateStream; return dupStream != null ? dupStream.Source : stream; } /// /// Flushes the source stream. /// public override void Flush() { this.source.Flush(); } /// /// Sets the length of the source stream. /// /// The desired length of the stream in bytes. public override void SetLength(long value) { this.source.SetLength(value); } /// /// Closes the underlying stream, effectively closing ALL duplicates. /// public override void Close() { this.source.Close(); } /// /// Reads from the source stream while maintaining a separate position /// and not impacting the source stream's position. /// /// An array of bytes. When this method returns, the buffer /// contains the specified byte array with the values between offset and /// (offset + count - 1) replaced by the bytes read from the current source. /// The zero-based byte offset in buffer at which to begin /// storing the data read from the current stream. /// The maximum number of bytes to be read from the current stream. /// 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. public override int Read(byte[] buffer, int offset, int count) { long saveSourcePosition = this.source.Position; this.source.Position = this.position; int read = this.source.Read(buffer, offset, count); this.position = this.source.Position; this.source.Position = saveSourcePosition; return read; } /// /// Writes to the source stream while maintaining a separate position /// and not impacting the source stream's position. /// /// An array of bytes. This method copies count /// bytes from buffer to the current stream. /// The zero-based byte offset in buffer at which /// to begin copying bytes to the current stream. /// The number of bytes to be written to the /// current stream. public override void Write(byte[] buffer, int offset, int count) { long saveSourcePosition = this.source.Position; this.source.Position = this.position; this.source.Write(buffer, offset, count); this.position = this.source.Position; this.source.Position = saveSourcePosition; } /// /// Changes the position of this stream without impacting the /// source stream's position. /// /// A byte offset relative to the origin parameter. /// A value of type SeekOrigin indicating the reference /// point used to obtain the new position. /// The new position within the current stream. public override long Seek(long offset, SeekOrigin origin) { long originPosition = 0; if (origin == SeekOrigin.Current) { originPosition = this.position; } else if (origin == SeekOrigin.End) { originPosition = this.Length; } this.position = originPosition + offset; return this.position; } } }