// 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; /// /// Wraps a source stream and offsets all read/write/seek calls by a given value. /// /// /// This class is used to trick archive an packing or unpacking process /// into reading or writing at an offset into a file, primarily for /// self-extracting packages. /// public class OffsetStream : Stream { private Stream source; private long sourceOffset; /// /// Creates a new OffsetStream instance from a source stream /// and using a specified offset. /// /// Underlying stream for which all calls will be offset. /// Positive or negative number of bytes to offset. public OffsetStream(Stream source, long offset) { if (source == null) { throw new ArgumentNullException("source"); } this.source = source; this.sourceOffset = offset; this.source.Seek(this.sourceOffset, SeekOrigin.Current); } /// /// Gets the underlying stream that this OffsetStream calls into. /// public Stream Source { get { return this.source; } } /// /// Gets the number of bytes to offset all calls before /// redirecting to the underlying stream. /// public long Offset { get { return this.sourceOffset; } } /// /// 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 effective length of the stream, which is equal to /// the length of the source stream minus the offset. /// public override long Length { get { return this.source.Length - this.sourceOffset; } } /// /// Gets or sets the effective position of the stream, which /// is equal to the position of the source stream minus the offset. /// public override long Position { get { return this.source.Position - this.sourceOffset; } set { this.source.Position = value + this.sourceOffset; } } /// /// Reads a sequence of bytes from the source stream and advances /// the position within the stream by the number of bytes read. /// /// 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) { return this.source.Read(buffer, offset, count); } /// /// Writes a sequence of bytes to the source stream and advances the /// current position within this stream by the number of bytes written. /// /// 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) { this.source.Write(buffer, offset, count); } /// /// Reads a byte from the stream and advances the position within the /// source stream by one byte, or returns -1 if at the end of the stream. /// /// The unsigned byte cast to an Int32, or -1 if at the /// end of the stream. public override int ReadByte() { return this.source.ReadByte(); } /// /// Writes a byte to the current position in the source stream and /// advances the position within the stream by one byte. /// /// The byte to write to the stream. public override void WriteByte(byte value) { this.source.WriteByte(value); } /// /// Flushes the source stream. /// public override void Flush() { this.source.Flush(); } /// /// Sets the position within the current stream, which is /// equal to the position within the source stream minus the offset. /// /// 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) { return this.source.Seek(offset + (origin == SeekOrigin.Begin ? this.sourceOffset : 0), origin) - this.sourceOffset; } /// /// Sets the effective length of the stream, which is equal to /// the length of the source stream minus the offset. /// /// The desired length of the /// current stream in bytes. public override void SetLength(long value) { this.source.SetLength(value + this.sourceOffset); } /// /// Closes the underlying stream. /// public override void Close() { this.source.Close(); } } }