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