// 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();
}
}
}