Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / CompressStream.cs / 1 / CompressStream.cs
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// Emulates a fully functional stream that persists using the Deflate compression algorithm
//
// This class provides a fully functional Stream on a restricted functionality compression
// stream (System.IO.Compression.DeflateStream).
//
// CompressStream operates in "transparent" mode (ReadThrough or WriteThrough) as long as possible for efficiency,
// reverting to full emulation mode as required to satisfy Stream requests that would violate the capabilities
// of the DeflateStream that actually does the reading or writing (decompress or compress). Emulation
// mode is implemented by class CompressEmulationStream.
//
// Note that the reason we need these modes is that DeflateStream is entirely modal in nature once
// constructed. If it is created in "compress" mode, it can only be used for compression. If it is
// opened in "decompress" mode, it can only be used for decompression. This means that Reading is only
// natively support in decompress mode, and writing is only natively supported in compress mode.
//
// Notes:
// If baseStream is non-seekable and non-readable it is not possible to enter Emulation mode. In this case
// we need to throw appropriate exception.
//
// History:
// 08/02/2004: BruceMac: Initial implementation.
//-----------------------------------------------------------------------------
using System;
using System.IO;
using System.IO.Compression; // for DeflateStream
using System.Diagnostics;
using System.IO.Packaging;
using MS.Internal.IO.Zip;
using System.Windows;
namespace MS.Internal.IO.Packaging
{
//-----------------------------------------------------
//
// Internal Members
//
//-----------------------------------------------------
///
/// Emulates a fully functional stream that persists using the Deflate compression algorithm
///
/// Attempts to provide ReadThrough or WriteThrough functionality as possible. If not possible,
/// a CompressEmulationStream is created and work is delegated to that class.
internal class CompressStream : Stream
{
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Stream Methods
///
/// Return the bytes requested from the container
///
/// destination buffer
/// offset to write into that buffer
/// how many bytes requested
/// how many bytes were written into .
///
/// The underlying stream, expected to be a DeflateStream or a CompressEmulationStream,
/// is in charge of leaving the IO position unchanged in case of an exception.
///
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
// no-op
if (count == 0)
return 0;
checked // catch any integer overflows
{
switch (_mode)
{
case Mode.Start:
{
// skip to the correct logical position if necessary (DeflateStream starts at position zero)
if (_position == 0)
{
// enter ReadPassThrough mode if it is efficient
ChangeMode(Mode.ReadPassThrough);
}
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.ReadPassThrough: // continue in ReadPassThrough mode
case Mode.Emulation: // continue to read from existing emulation stream
{
break;
}
case Mode.WritePassThrough: // enter Emulation mode
{
// optimization - if they are trying to jump back to the start to read, simply jump to ReadPassThrough mode
if (_position == 0)
ChangeMode(Mode.ReadPassThrough);
else
ChangeMode(Mode.Emulation);
break;
}
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
// we might be in Start mode now if we are beyond the end of stream - just return zero
if (_current == null)
return 0;
int bytesRead = _current.Read(buffer, offset, count);
// optimization for ReadPassThrough mode - we actually know the length because we ran out of bytes
if (_mode == Mode.ReadPassThrough && bytesRead == 0)
{
// possible first chance to set and verify length from header against real data length
UpdateUncompressedDataLength(_position);
// since we've exhausted the deflateStream, discard it to reduce working set
ChangeMode(Mode.Start);
}
// Stream contract - don't update position until we are certain that no exceptions have occurred
_position += bytesRead;
return bytesRead;
}
}
///
/// Write
///
/// Note that zero length write to deflate stream actually results in a stream containing 2 bytes. This is
/// required to maintain compatibility with the standard.
public override void Write(byte[] buffer, int offset, int count)
{
CheckDisposed();
PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
// no-op
if (count == 0)
return;
checked
{
switch (_mode)
{
case Mode.Start: // enter WritePassThrough mode if possible
{
// Special case: If stream has existing content, we need to go straight
// to Emulation mode otherwise we'll potentially destroy existing data.
// Don't bother entering WritePassThroughMode if position is non-zero because
// we'll just enter emulation later.
if (_position == 0 && IsDeflateStreamEmpty(_baseStream))
ChangeMode(Mode.WritePassThrough);
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.WritePassThrough: // continue in Write mode
case Mode.Emulation: // continue to read from existing emulation stream
{
break;
}
case Mode.ReadPassThrough: // enter Emulation mode
{
ChangeMode(Mode.Emulation); break;
}
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
_current.Write(buffer, offset, count);
_position += count;
}
// keep track of the current length in case someone asks for it
if (_mode == Mode.WritePassThrough)
CachedLength = _position;
_dirtyForFlushing= true;
_dirtyForClosing= true;
}
///
/// Seek
///
/// offset
/// origin
/// zero
public override long Seek(long offset, SeekOrigin origin)
{
CheckDisposed();
if (!CanSeek)
throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
checked
{
// Calculate newPos
// If origin is Begin or Current newPos can be calculated without knowing
// the stream length. If origin is End, switch to Emulation immediately.
long newPos = -1;
switch (origin)
{
case SeekOrigin.Begin: newPos = offset; break;
case SeekOrigin.Current: newPos = _position + offset; break;
case SeekOrigin.End:
ChangeMode(Mode.Emulation); // has no effect if already in Emulation mode
newPos = Length + offset; // Length is now legal to call
break;
}
// we have a reliable newPos now - throw if its illegal
if (newPos < 0)
throw new ArgumentException(SR.Get(SRID.SeekNegative));
// is the new position any different than the current position?
long delta = newPos - _position;
if (delta == 0)
return _position;
// We optimize for very restricted case - short seek forward in read-only mode.
// This prevents the expense of entering Emulation mode when a stream reader is
// skipping a few bytes while parsing binary data structures (for example).
if ((delta > 0) && (delta < _readPassThroughModeSeekThreshold)
&& (_mode == Mode.ReadPassThrough))
{
// We're able to fake the seek by reading in this one corner case.
// We cannot be in ReadPassThroughMode if currently beyond end of physical
// data so it is safe to assume that the value returned from
// this call represents real data.
long bytesNotRead = ReadPassThroughModeSeek(delta);
if (bytesNotRead > 0)
{
// Stream was exhausted - seek was beyond end of physical
// stream so we need to update our cachedLength and
// move to Start mode.
UpdateUncompressedDataLength(newPos - bytesNotRead);
ChangeMode(Mode.Start);
}
}
else
{
// Enter Emulation for efficiency
ChangeMode(Mode.Emulation); // No-op if already in Emulation
_current.Position = newPos; // Update to new value
}
// update logical position
_position = newPos;
}
return _position;
}
///
/// SetLength
///
public override void SetLength(long newLength)
{
CheckDisposed();
if (!CanSeek)
throw new NotSupportedException(SR.Get(SRID.SetLengthNotSupported));
_lengthVerified = true; // no longer need to verify our length against our constructor value
switch (_mode)
{
case Mode.Start:
case Mode.WritePassThrough:
case Mode.ReadPassThrough:
{
// optimize for "clear the whole stream" - no need to enter emulation
if (newLength == 0)
{
ChangeMode(Mode.Start); // discard any existing deflate stream
_baseStream.SetLength(0); // clear the underlying stream
UpdateUncompressedDataLength(newLength);
}
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.Emulation: break;
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
if (_mode == Mode.Emulation)
_current.SetLength(newLength);
// position seek pointer appropriately
if (newLength < _position)
Seek(newLength, SeekOrigin.Begin);
// still need to mark ourselves dirty so that our caller can get the correct result
// when they query the IsDirty property
_dirtyForFlushing= true;
_dirtyForClosing= true;
}
///
/// Flush
///
/// Flushes to stream (if necessary)
public override void Flush()
{
CheckDisposed();
// Always pass through to subordinates because they may be caching things (ignore _dirty flag here).
// Current must be non-null if changes have been made.
if (_current != null)
{
_current.Flush();
_dirtyForFlushing = false; // extra flushes after this will not produce more data
// avoid clearing flag when we are empty because it would prevent generation
// of the 2-byte sequence on dispose
if ((_mode == Mode.Emulation) && (Length != 0))
{
_dirtyForClosing = false; // if it is ReadThrough or Start (it shouldn't be dirty in the first place)
// if it is WriteThrough it is going to be dirty untill it is closed
}
}
_baseStream.Flush();
}
#endregion Stream Methods
#region Stream Properties
///
/// Current logical position within the stream
///
public override long Position
{
get
{
CheckDisposed();
return _position;
}
set
{
CheckDisposed();
// convert to a Seek so we don't have to replicate the Seek logic here
Seek(checked(value - _position), SeekOrigin.Current);
}
}
///
/// Length
///
public override long Length
{
get
{
CheckDisposed();
// if (!CanSeek)
// throw new NotSupportedException(SR.Get(SRID.LengthNotSupported));
switch (_mode)
{
case Mode.Start:
case Mode.WritePassThrough:
case Mode.ReadPassThrough:
{
// use cached length if possible
if (CachedLength >= 0)
return CachedLength;
else
{
// Special optimization for new/empty streams - avoid entering Emulation as long as possible.
if (_position == 0 && IsDeflateStreamEmpty(_baseStream))
return 0;
ChangeMode(Mode.Emulation);
}
break;
}
case Mode.Emulation: break;
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
// must be in Emulation mode to get here
// possible first chance to verify length from header against real data length
UpdateUncompressedDataLength(_current.Length);
return _current.Length;
}
}
///
/// Is stream readable?
///
/// returns false when called on disposed stream
public override bool CanRead
{
get
{
// cannot read from a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanRead;
}
}
///
/// Is stream seekable - should be handled by our owner
///
/// returns false when called on disposed stream
public override bool CanSeek
{
get
{
// cannot seek on a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanSeek;
}
}
///
/// Is stream writeable?
///
/// returns false when called on disposed stream
public override bool CanWrite
{
get
{
// cannot write to a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanWrite;
}
}
#endregion
#region Internal
//------------------------------------------------------
//
// Internal Constructors
//
//------------------------------------------------------
///
/// Constructor
///
/// uncompressed length if known, or -1 if not known
/// part stream
internal CompressStream(Stream baseStream, long length) : this (baseStream, length, false)
{
}
///
/// Constructor
///
/// part stream
/// new stream or not?
/// uncompressed length if known, or -1 if not known
internal CompressStream(Stream baseStream, long length, bool creating)
{
if (baseStream == null)
throw new ArgumentNullException("baseStream");
if (length < -1)
throw new ArgumentOutOfRangeException("length");
_baseStream = baseStream;
_cachedLength = length;
Debug.Assert(_baseStream.Position == 0,
"Our logic assumes position zero and we don't seek because sometimes it's not supported");
// we need to be dirty if this is a new stream because an empty deflate
// stream actually causes a write (this happens only on close ); therefore
// we are dirty for close (in case of creation) and not dirty for flush
_dirtyForFlushing= false;
_dirtyForClosing= creating;
}
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
///
/// IsDirty
///
///
internal bool IsDirty(bool closingFlag)
{
return closingFlag ? _dirtyForClosing : _dirtyForFlushing;
}
///
/// IsDisposed
///
///
internal bool IsDisposed
{
get
{
return (_mode == Mode.Disposed);
}
}
#endregion
#region Protected
//-----------------------------------------------------
//
// Protected Methods
//
//-----------------------------------------------------
///
/// Dispose(bool)
///
///
/// We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to
/// call Dispose() instead of Close().
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_mode != Mode.Disposed)
{
Flush();
if (_current != null)
{
_current.Close(); // call Dispose()
_current = null;
}
// Special handling for "empty" deflated streams - they actually persist
// a 2 byte sequence.
// Three separate cases (assuming the stream is dirty):
// 1) Stream is seekable - check Length and write the 2-byte sequence
// if the stream is empty.
// 2) Stream is non-seekable and negative CachedLength - this means we were created
// (not opened) and there have been no writes so we need the 2-byte sequence.
// 3) Stream is non-seekable and zero CachedLength - this means we are
// really zero-bytes long which indicates we need the 2-byte sequence.
if (_dirtyForClosing && ((_baseStream.CanSeek && _baseStream.Length == 0) ||
(_cachedLength <= 0)))
{
_baseStream.Write(_emptyDeflateStreamConstant, 0, 2);
_baseStream.Flush();
}
// _baseStream.Close(); // never close a stream we do not own
_baseStream = null;
ChangeMode(Mode.Disposed);
_dirtyForClosing = false;
_dirtyForFlushing = false;
}
}
}
finally
{
base.Dispose(disposing);
}
}
#endregion
// Changed the mode from Emulation to Start
internal void Reset()
{
CheckDisposed();
ChangeMode(Mode.Start);
}
#region Private
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
///
/// Verify Uncompressed length from data against what we were given in the constructor
///
///
/// verify length from header against real data length
private void UpdateUncompressedDataLength(long dataLength)
{
Debug.Assert(dataLength >= 0);
// only compare if we have a value
if (_cachedLength >= 0)
{
if (!_lengthVerified)
{
if (_cachedLength != dataLength)
throw new FileFormatException(SR.Get(SRID.CompressLengthMismatch));
_lengthVerified = true;
}
}
_cachedLength = dataLength; // always set
}
///
/// Helper method to reduce complexity in the public Seek method
///
///
/// bytes remaining - will be non-zero if stream was exhausted
/// Attempts to "seek" by reading an discarding bytes using the current
/// Decompressing DeflateStream.
/// _position is updated by our caller - this function does not change it
private long ReadPassThroughModeSeek(long bytesToSeek)
{
checked
{
Debug.Assert(bytesToSeek > 0, "Logic Error - bytesToSeek should be positive");
// allocate buffer just big enough for the seek, maximum of 4k
byte[] buf = new byte[Math.Min(0x1000, bytesToSeek)];
// read to simulate Seek
while (bytesToSeek > 0)
{
// don't exceed the buffer size
long n = Math.Min(bytesToSeek, buf.Length);
n = _current.Read(buf, 0, (int)n);
// seek beyond end of stream is legal
if (n == 0)
{
break; // just exit
}
bytesToSeek -= n;
}
// return bytes not read
return bytesToSeek;
}
}
///
/// Call this before accepting any public API call (except some Stream calls that
/// are allowed to respond even when Closed
///
private void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed));
}
///
/// ChangeMode
///
///
/// Does not update Position of _current for change to ReadPassThroughMode.
private void ChangeMode(Mode newMode)
{
// ignore redundant calls (allowing these actually simplifies the logic in SetLength)
if (newMode == _mode)
return;
// every state change requires this logic
if (_current != null)
{
_current.Close();
_dirtyForClosing = false;
_dirtyForFlushing = false;
}
// set the new mode - must be done before the call to Seek
_mode = newMode;
switch (newMode)
{
case Mode.Start:
{
_current = null;
_baseStream.Position = 0;
break;
}
case Mode.ReadPassThrough:
case Mode.WritePassThrough:
{
Debug.Assert(_baseStream.Position == 0);
// create the appropriate DeflateStream
_current = new DeflateStream(_baseStream,
newMode == Mode.WritePassThrough ? CompressionMode.Compress : CompressionMode.Decompress,
true);
break;
}
case Mode.Emulation:
{
// Create emulation stream. Use a MemoryStream for local caching.
// Do not change this logic for RM cases because the data is "in the clear" and must
// not be persisted in a vulnerable location.
SparseMemoryStream memStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark);
_current = new CompressEmulationStream(_baseStream, memStream, _position, new DeflateEmulationTransform());
// verify and set length
UpdateUncompressedDataLength(_current.Length);
break;
}
case Mode.Disposed: break;
default:
Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
}
///
/// Call this to determine if a deflate stream is empty - pass the actual compressed stream
///
///
/// true if empty
private static bool IsDeflateStreamEmpty(Stream s)
{
bool empty = false;
// Special case: If stream has existing content, we need to go straight
// to Emulation mode otherwise we'll potentially destroy existing data.
// This will not be possible if the base stream is write-only and non-seekable.
// The minimal length of a persisted DeflateStream is 2 so if the length
// is 2, we can safely overwrite. We explicitly call Deflate on a stream of length
// 1 so that we can get a consistent exception because this will be an illegally
// compressed stream.
if (s.CanSeek && s.CanRead)
{
Debug.Assert(s.Position == 0);
// read the two bytes and commpare to the known 2 bytes that represent
// and empty deflate stream
byte[] buf = new byte[2];
int bytesRead = s.Read(buf, 0, 2);
empty = ((bytesRead == 0) ||
(buf[0] == _emptyDeflateStreamConstant[0] && buf[1] == _emptyDeflateStreamConstant[1]));
s.Position = 0; // restore position
}
else
empty = true; // if write-time-streaming we're going to destroy what's there anyway
return empty;
}
private long CachedLength
{
get
{
// only maintained when NOT in Emulation mode
Debug.Assert(_mode != Mode.Emulation, "Logic error: CachedLength not maintained in Emulation mode - illegal Get");
return _cachedLength;
}
set
{
// only maintained when NOT in Emulation mode
Debug.Assert(_mode != Mode.Emulation, "Logic error: CachedLength not maintained in Emulation mode - illegal Set");
Debug.Assert(value >= 0, "Length cannot be negative - logic error?");
_cachedLength = value;
}
}
//------------------------------------------------------
//
// Private Variables
//
//-----------------------------------------------------
// Add explicit values to these enum variables because we do some arithmetic with them and don't want to
// rely on the default behavior.
private enum Mode
{
Start = 0, // we have no outstanding memory in use - state on construction
ReadPassThrough = 1, // we are able to read from the current position
WritePassThrough = 2, // we are able to write to the current position
Emulation = 3, // we have moved all data to a memory stream and all operations are supported
Disposed = 4 // we are disposed
};
private Mode _mode; // current stream mode
private Int64 _position; // current logical position - only copy - shared with all helpers
private Stream _baseStream; // stream we ultimately decompress from and to in the container
private Stream _current; // current stream object
private bool _dirtyForFlushing; // are we dirty, these 2 flags are going to differ in the case of the FLushed Write Through mode
private bool _dirtyForClosing; // _dirtyForFlushing will be false (meaning that there is no data to be flushed) while
// _dirtyForClosing will be true as there might be some data that need to be added for closing
// Note: DirtyForFlushing can never be true when DirtyForClosing is false.
private bool _lengthVerified; // true if we have successfully compared the length given in our constructor against that obtained from
// actually decompressing the data
private long _cachedLength; // cached value prevents us from entering Emulation to obtain length after ReadPassThrough reads all bytes
// -1 means not set
// this is what is persisted when a deflate stream is of length zero
private static byte[] _emptyDeflateStreamConstant = new byte[] { 0x03, 0x00 };
private const long _lowWaterMark = 0x19000; // we definately would like to keep everythuing under 100 KB in memory
private const long _highWaterMark = 0xA00000; // we would like to keep everything over 10 MB on disk
private const long _readPassThroughModeSeekThreshold = 0x40; // amount we can seek in a reasonable amount of time while decompressing
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// Emulates a fully functional stream that persists using the Deflate compression algorithm
//
// This class provides a fully functional Stream on a restricted functionality compression
// stream (System.IO.Compression.DeflateStream).
//
// CompressStream operates in "transparent" mode (ReadThrough or WriteThrough) as long as possible for efficiency,
// reverting to full emulation mode as required to satisfy Stream requests that would violate the capabilities
// of the DeflateStream that actually does the reading or writing (decompress or compress). Emulation
// mode is implemented by class CompressEmulationStream.
//
// Note that the reason we need these modes is that DeflateStream is entirely modal in nature once
// constructed. If it is created in "compress" mode, it can only be used for compression. If it is
// opened in "decompress" mode, it can only be used for decompression. This means that Reading is only
// natively support in decompress mode, and writing is only natively supported in compress mode.
//
// Notes:
// If baseStream is non-seekable and non-readable it is not possible to enter Emulation mode. In this case
// we need to throw appropriate exception.
//
// History:
// 08/02/2004: BruceMac: Initial implementation.
//-----------------------------------------------------------------------------
using System;
using System.IO;
using System.IO.Compression; // for DeflateStream
using System.Diagnostics;
using System.IO.Packaging;
using MS.Internal.IO.Zip;
using System.Windows;
namespace MS.Internal.IO.Packaging
{
//-----------------------------------------------------
//
// Internal Members
//
//-----------------------------------------------------
///
/// Emulates a fully functional stream that persists using the Deflate compression algorithm
///
/// Attempts to provide ReadThrough or WriteThrough functionality as possible. If not possible,
/// a CompressEmulationStream is created and work is delegated to that class.
internal class CompressStream : Stream
{
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Stream Methods
///
/// Return the bytes requested from the container
///
/// destination buffer
/// offset to write into that buffer
/// how many bytes requested
/// how many bytes were written into .
///
/// The underlying stream, expected to be a DeflateStream or a CompressEmulationStream,
/// is in charge of leaving the IO position unchanged in case of an exception.
///
public override int Read(byte[] buffer, int offset, int count)
{
CheckDisposed();
PackagingUtilities.VerifyStreamReadArgs(this, buffer, offset, count);
// no-op
if (count == 0)
return 0;
checked // catch any integer overflows
{
switch (_mode)
{
case Mode.Start:
{
// skip to the correct logical position if necessary (DeflateStream starts at position zero)
if (_position == 0)
{
// enter ReadPassThrough mode if it is efficient
ChangeMode(Mode.ReadPassThrough);
}
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.ReadPassThrough: // continue in ReadPassThrough mode
case Mode.Emulation: // continue to read from existing emulation stream
{
break;
}
case Mode.WritePassThrough: // enter Emulation mode
{
// optimization - if they are trying to jump back to the start to read, simply jump to ReadPassThrough mode
if (_position == 0)
ChangeMode(Mode.ReadPassThrough);
else
ChangeMode(Mode.Emulation);
break;
}
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
// we might be in Start mode now if we are beyond the end of stream - just return zero
if (_current == null)
return 0;
int bytesRead = _current.Read(buffer, offset, count);
// optimization for ReadPassThrough mode - we actually know the length because we ran out of bytes
if (_mode == Mode.ReadPassThrough && bytesRead == 0)
{
// possible first chance to set and verify length from header against real data length
UpdateUncompressedDataLength(_position);
// since we've exhausted the deflateStream, discard it to reduce working set
ChangeMode(Mode.Start);
}
// Stream contract - don't update position until we are certain that no exceptions have occurred
_position += bytesRead;
return bytesRead;
}
}
///
/// Write
///
/// Note that zero length write to deflate stream actually results in a stream containing 2 bytes. This is
/// required to maintain compatibility with the standard.
public override void Write(byte[] buffer, int offset, int count)
{
CheckDisposed();
PackagingUtilities.VerifyStreamWriteArgs(this, buffer, offset, count);
// no-op
if (count == 0)
return;
checked
{
switch (_mode)
{
case Mode.Start: // enter WritePassThrough mode if possible
{
// Special case: If stream has existing content, we need to go straight
// to Emulation mode otherwise we'll potentially destroy existing data.
// Don't bother entering WritePassThroughMode if position is non-zero because
// we'll just enter emulation later.
if (_position == 0 && IsDeflateStreamEmpty(_baseStream))
ChangeMode(Mode.WritePassThrough);
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.WritePassThrough: // continue in Write mode
case Mode.Emulation: // continue to read from existing emulation stream
{
break;
}
case Mode.ReadPassThrough: // enter Emulation mode
{
ChangeMode(Mode.Emulation); break;
}
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
_current.Write(buffer, offset, count);
_position += count;
}
// keep track of the current length in case someone asks for it
if (_mode == Mode.WritePassThrough)
CachedLength = _position;
_dirtyForFlushing= true;
_dirtyForClosing= true;
}
///
/// Seek
///
/// offset
/// origin
/// zero
public override long Seek(long offset, SeekOrigin origin)
{
CheckDisposed();
if (!CanSeek)
throw new NotSupportedException(SR.Get(SRID.SeekNotSupported));
checked
{
// Calculate newPos
// If origin is Begin or Current newPos can be calculated without knowing
// the stream length. If origin is End, switch to Emulation immediately.
long newPos = -1;
switch (origin)
{
case SeekOrigin.Begin: newPos = offset; break;
case SeekOrigin.Current: newPos = _position + offset; break;
case SeekOrigin.End:
ChangeMode(Mode.Emulation); // has no effect if already in Emulation mode
newPos = Length + offset; // Length is now legal to call
break;
}
// we have a reliable newPos now - throw if its illegal
if (newPos < 0)
throw new ArgumentException(SR.Get(SRID.SeekNegative));
// is the new position any different than the current position?
long delta = newPos - _position;
if (delta == 0)
return _position;
// We optimize for very restricted case - short seek forward in read-only mode.
// This prevents the expense of entering Emulation mode when a stream reader is
// skipping a few bytes while parsing binary data structures (for example).
if ((delta > 0) && (delta < _readPassThroughModeSeekThreshold)
&& (_mode == Mode.ReadPassThrough))
{
// We're able to fake the seek by reading in this one corner case.
// We cannot be in ReadPassThroughMode if currently beyond end of physical
// data so it is safe to assume that the value returned from
// this call represents real data.
long bytesNotRead = ReadPassThroughModeSeek(delta);
if (bytesNotRead > 0)
{
// Stream was exhausted - seek was beyond end of physical
// stream so we need to update our cachedLength and
// move to Start mode.
UpdateUncompressedDataLength(newPos - bytesNotRead);
ChangeMode(Mode.Start);
}
}
else
{
// Enter Emulation for efficiency
ChangeMode(Mode.Emulation); // No-op if already in Emulation
_current.Position = newPos; // Update to new value
}
// update logical position
_position = newPos;
}
return _position;
}
///
/// SetLength
///
public override void SetLength(long newLength)
{
CheckDisposed();
if (!CanSeek)
throw new NotSupportedException(SR.Get(SRID.SetLengthNotSupported));
_lengthVerified = true; // no longer need to verify our length against our constructor value
switch (_mode)
{
case Mode.Start:
case Mode.WritePassThrough:
case Mode.ReadPassThrough:
{
// optimize for "clear the whole stream" - no need to enter emulation
if (newLength == 0)
{
ChangeMode(Mode.Start); // discard any existing deflate stream
_baseStream.SetLength(0); // clear the underlying stream
UpdateUncompressedDataLength(newLength);
}
else
ChangeMode(Mode.Emulation);
break;
}
case Mode.Emulation: break;
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
if (_mode == Mode.Emulation)
_current.SetLength(newLength);
// position seek pointer appropriately
if (newLength < _position)
Seek(newLength, SeekOrigin.Begin);
// still need to mark ourselves dirty so that our caller can get the correct result
// when they query the IsDirty property
_dirtyForFlushing= true;
_dirtyForClosing= true;
}
///
/// Flush
///
/// Flushes to stream (if necessary)
public override void Flush()
{
CheckDisposed();
// Always pass through to subordinates because they may be caching things (ignore _dirty flag here).
// Current must be non-null if changes have been made.
if (_current != null)
{
_current.Flush();
_dirtyForFlushing = false; // extra flushes after this will not produce more data
// avoid clearing flag when we are empty because it would prevent generation
// of the 2-byte sequence on dispose
if ((_mode == Mode.Emulation) && (Length != 0))
{
_dirtyForClosing = false; // if it is ReadThrough or Start (it shouldn't be dirty in the first place)
// if it is WriteThrough it is going to be dirty untill it is closed
}
}
_baseStream.Flush();
}
#endregion Stream Methods
#region Stream Properties
///
/// Current logical position within the stream
///
public override long Position
{
get
{
CheckDisposed();
return _position;
}
set
{
CheckDisposed();
// convert to a Seek so we don't have to replicate the Seek logic here
Seek(checked(value - _position), SeekOrigin.Current);
}
}
///
/// Length
///
public override long Length
{
get
{
CheckDisposed();
// if (!CanSeek)
// throw new NotSupportedException(SR.Get(SRID.LengthNotSupported));
switch (_mode)
{
case Mode.Start:
case Mode.WritePassThrough:
case Mode.ReadPassThrough:
{
// use cached length if possible
if (CachedLength >= 0)
return CachedLength;
else
{
// Special optimization for new/empty streams - avoid entering Emulation as long as possible.
if (_position == 0 && IsDeflateStreamEmpty(_baseStream))
return 0;
ChangeMode(Mode.Emulation);
}
break;
}
case Mode.Emulation: break;
default: Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
// must be in Emulation mode to get here
// possible first chance to verify length from header against real data length
UpdateUncompressedDataLength(_current.Length);
return _current.Length;
}
}
///
/// Is stream readable?
///
/// returns false when called on disposed stream
public override bool CanRead
{
get
{
// cannot read from a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanRead;
}
}
///
/// Is stream seekable - should be handled by our owner
///
/// returns false when called on disposed stream
public override bool CanSeek
{
get
{
// cannot seek on a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanSeek;
}
}
///
/// Is stream writeable?
///
/// returns false when called on disposed stream
public override bool CanWrite
{
get
{
// cannot write to a close stream, but don't throw if asked
return (_mode != Mode.Disposed) && _baseStream.CanWrite;
}
}
#endregion
#region Internal
//------------------------------------------------------
//
// Internal Constructors
//
//------------------------------------------------------
///
/// Constructor
///
/// uncompressed length if known, or -1 if not known
/// part stream
internal CompressStream(Stream baseStream, long length) : this (baseStream, length, false)
{
}
///
/// Constructor
///
/// part stream
/// new stream or not?
/// uncompressed length if known, or -1 if not known
internal CompressStream(Stream baseStream, long length, bool creating)
{
if (baseStream == null)
throw new ArgumentNullException("baseStream");
if (length < -1)
throw new ArgumentOutOfRangeException("length");
_baseStream = baseStream;
_cachedLength = length;
Debug.Assert(_baseStream.Position == 0,
"Our logic assumes position zero and we don't seek because sometimes it's not supported");
// we need to be dirty if this is a new stream because an empty deflate
// stream actually causes a write (this happens only on close ); therefore
// we are dirty for close (in case of creation) and not dirty for flush
_dirtyForFlushing= false;
_dirtyForClosing= creating;
}
//-----------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
///
/// IsDirty
///
///
internal bool IsDirty(bool closingFlag)
{
return closingFlag ? _dirtyForClosing : _dirtyForFlushing;
}
///
/// IsDisposed
///
///
internal bool IsDisposed
{
get
{
return (_mode == Mode.Disposed);
}
}
#endregion
#region Protected
//-----------------------------------------------------
//
// Protected Methods
//
//-----------------------------------------------------
///
/// Dispose(bool)
///
///
/// We implement this because we want a consistent experience (essentially Flush our data) if the user chooses to
/// call Dispose() instead of Close().
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
if (_mode != Mode.Disposed)
{
Flush();
if (_current != null)
{
_current.Close(); // call Dispose()
_current = null;
}
// Special handling for "empty" deflated streams - they actually persist
// a 2 byte sequence.
// Three separate cases (assuming the stream is dirty):
// 1) Stream is seekable - check Length and write the 2-byte sequence
// if the stream is empty.
// 2) Stream is non-seekable and negative CachedLength - this means we were created
// (not opened) and there have been no writes so we need the 2-byte sequence.
// 3) Stream is non-seekable and zero CachedLength - this means we are
// really zero-bytes long which indicates we need the 2-byte sequence.
if (_dirtyForClosing && ((_baseStream.CanSeek && _baseStream.Length == 0) ||
(_cachedLength <= 0)))
{
_baseStream.Write(_emptyDeflateStreamConstant, 0, 2);
_baseStream.Flush();
}
// _baseStream.Close(); // never close a stream we do not own
_baseStream = null;
ChangeMode(Mode.Disposed);
_dirtyForClosing = false;
_dirtyForFlushing = false;
}
}
}
finally
{
base.Dispose(disposing);
}
}
#endregion
// Changed the mode from Emulation to Start
internal void Reset()
{
CheckDisposed();
ChangeMode(Mode.Start);
}
#region Private
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
///
/// Verify Uncompressed length from data against what we were given in the constructor
///
///
/// verify length from header against real data length
private void UpdateUncompressedDataLength(long dataLength)
{
Debug.Assert(dataLength >= 0);
// only compare if we have a value
if (_cachedLength >= 0)
{
if (!_lengthVerified)
{
if (_cachedLength != dataLength)
throw new FileFormatException(SR.Get(SRID.CompressLengthMismatch));
_lengthVerified = true;
}
}
_cachedLength = dataLength; // always set
}
///
/// Helper method to reduce complexity in the public Seek method
///
///
/// bytes remaining - will be non-zero if stream was exhausted
/// Attempts to "seek" by reading an discarding bytes using the current
/// Decompressing DeflateStream.
/// _position is updated by our caller - this function does not change it
private long ReadPassThroughModeSeek(long bytesToSeek)
{
checked
{
Debug.Assert(bytesToSeek > 0, "Logic Error - bytesToSeek should be positive");
// allocate buffer just big enough for the seek, maximum of 4k
byte[] buf = new byte[Math.Min(0x1000, bytesToSeek)];
// read to simulate Seek
while (bytesToSeek > 0)
{
// don't exceed the buffer size
long n = Math.Min(bytesToSeek, buf.Length);
n = _current.Read(buf, 0, (int)n);
// seek beyond end of stream is legal
if (n == 0)
{
break; // just exit
}
bytesToSeek -= n;
}
// return bytes not read
return bytesToSeek;
}
}
///
/// Call this before accepting any public API call (except some Stream calls that
/// are allowed to respond even when Closed
///
private void CheckDisposed()
{
if (IsDisposed)
throw new ObjectDisposedException(null, SR.Get(SRID.StreamObjectDisposed));
}
///
/// ChangeMode
///
///
/// Does not update Position of _current for change to ReadPassThroughMode.
private void ChangeMode(Mode newMode)
{
// ignore redundant calls (allowing these actually simplifies the logic in SetLength)
if (newMode == _mode)
return;
// every state change requires this logic
if (_current != null)
{
_current.Close();
_dirtyForClosing = false;
_dirtyForFlushing = false;
}
// set the new mode - must be done before the call to Seek
_mode = newMode;
switch (newMode)
{
case Mode.Start:
{
_current = null;
_baseStream.Position = 0;
break;
}
case Mode.ReadPassThrough:
case Mode.WritePassThrough:
{
Debug.Assert(_baseStream.Position == 0);
// create the appropriate DeflateStream
_current = new DeflateStream(_baseStream,
newMode == Mode.WritePassThrough ? CompressionMode.Compress : CompressionMode.Decompress,
true);
break;
}
case Mode.Emulation:
{
// Create emulation stream. Use a MemoryStream for local caching.
// Do not change this logic for RM cases because the data is "in the clear" and must
// not be persisted in a vulnerable location.
SparseMemoryStream memStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark);
_current = new CompressEmulationStream(_baseStream, memStream, _position, new DeflateEmulationTransform());
// verify and set length
UpdateUncompressedDataLength(_current.Length);
break;
}
case Mode.Disposed: break;
default:
Debug.Assert(false, "Illegal state for CompressStream - logic error"); break;
}
}
///
/// Call this to determine if a deflate stream is empty - pass the actual compressed stream
///
///
/// true if empty
private static bool IsDeflateStreamEmpty(Stream s)
{
bool empty = false;
// Special case: If stream has existing content, we need to go straight
// to Emulation mode otherwise we'll potentially destroy existing data.
// This will not be possible if the base stream is write-only and non-seekable.
// The minimal length of a persisted DeflateStream is 2 so if the length
// is 2, we can safely overwrite. We explicitly call Deflate on a stream of length
// 1 so that we can get a consistent exception because this will be an illegally
// compressed stream.
if (s.CanSeek && s.CanRead)
{
Debug.Assert(s.Position == 0);
// read the two bytes and commpare to the known 2 bytes that represent
// and empty deflate stream
byte[] buf = new byte[2];
int bytesRead = s.Read(buf, 0, 2);
empty = ((bytesRead == 0) ||
(buf[0] == _emptyDeflateStreamConstant[0] && buf[1] == _emptyDeflateStreamConstant[1]));
s.Position = 0; // restore position
}
else
empty = true; // if write-time-streaming we're going to destroy what's there anyway
return empty;
}
private long CachedLength
{
get
{
// only maintained when NOT in Emulation mode
Debug.Assert(_mode != Mode.Emulation, "Logic error: CachedLength not maintained in Emulation mode - illegal Get");
return _cachedLength;
}
set
{
// only maintained when NOT in Emulation mode
Debug.Assert(_mode != Mode.Emulation, "Logic error: CachedLength not maintained in Emulation mode - illegal Set");
Debug.Assert(value >= 0, "Length cannot be negative - logic error?");
_cachedLength = value;
}
}
//------------------------------------------------------
//
// Private Variables
//
//-----------------------------------------------------
// Add explicit values to these enum variables because we do some arithmetic with them and don't want to
// rely on the default behavior.
private enum Mode
{
Start = 0, // we have no outstanding memory in use - state on construction
ReadPassThrough = 1, // we are able to read from the current position
WritePassThrough = 2, // we are able to write to the current position
Emulation = 3, // we have moved all data to a memory stream and all operations are supported
Disposed = 4 // we are disposed
};
private Mode _mode; // current stream mode
private Int64 _position; // current logical position - only copy - shared with all helpers
private Stream _baseStream; // stream we ultimately decompress from and to in the container
private Stream _current; // current stream object
private bool _dirtyForFlushing; // are we dirty, these 2 flags are going to differ in the case of the FLushed Write Through mode
private bool _dirtyForClosing; // _dirtyForFlushing will be false (meaning that there is no data to be flushed) while
// _dirtyForClosing will be true as there might be some data that need to be added for closing
// Note: DirtyForFlushing can never be true when DirtyForClosing is false.
private bool _lengthVerified; // true if we have successfully compared the length given in our constructor against that obtained from
// actually decompressing the data
private long _cachedLength; // cached value prevents us from entering Emulation to obtain length after ReadPassThrough reads all bytes
// -1 means not set
// this is what is persisted when a deflate stream is of length zero
private static byte[] _emptyDeflateStreamConstant = new byte[] { 0x03, 0x00 };
private const long _lowWaterMark = 0x19000; // we definately would like to keep everythuing under 100 KB in memory
private const long _highWaterMark = 0xA00000; // we would like to keep everything over 10 MB on disk
private const long _readPassThroughModeSeekThreshold = 0x40; // amount we can seek in a reasonable amount of time while decompressing
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TracePayload.cs
- AsnEncodedData.cs
- securitycriticaldataformultiplegetandset.cs
- AsyncCompletedEventArgs.cs
- AsyncOperationManager.cs
- ProjectionPruner.cs
- MenuItemStyle.cs
- DoubleAnimationBase.cs
- ControlCodeDomSerializer.cs
- RequestDescription.cs
- SMSvcHost.cs
- TreeViewDesigner.cs
- Bind.cs
- WebPageTraceListener.cs
- AxHost.cs
- DataMemberFieldEditor.cs
- RemotingException.cs
- AppDomainShutdownMonitor.cs
- GridViewHeaderRowPresenterAutomationPeer.cs
- XmlTypeMapping.cs
- DocumentXPathNavigator.cs
- GetWinFXPath.cs
- FormsAuthenticationModule.cs
- GradientStop.cs
- FragmentQueryProcessor.cs
- EventEntry.cs
- Source.cs
- HighlightVisual.cs
- XPathNodeIterator.cs
- XmlNotation.cs
- CreateUserWizardStep.cs
- GenerateScriptTypeAttribute.cs
- ObjectQueryProvider.cs
- ListDesigner.cs
- XmlSchemaSequence.cs
- Context.cs
- SafeNativeMethodsOther.cs
- AmbientValueAttribute.cs
- WhitespaceRuleReader.cs
- ScriptResourceInfo.cs
- XmlByteStreamReader.cs
- StylusPlugInCollection.cs
- UnaryQueryOperator.cs
- EventQueueState.cs
- InputLangChangeRequestEvent.cs
- DbProviderFactories.cs
- WebConvert.cs
- ChannelManager.cs
- LinqDataSourceView.cs
- RuntimeComponentFilter.cs
- CharStorage.cs
- DataGridViewLinkColumn.cs
- RadioButtonRenderer.cs
- AsyncCompletedEventArgs.cs
- DependencyPropertyKey.cs
- PKCS1MaskGenerationMethod.cs
- PropertyHelper.cs
- MemberInfoSerializationHolder.cs
- XmlHierarchicalDataSourceView.cs
- DbConnectionFactory.cs
- ReachPrintTicketSerializerAsync.cs
- SmtpException.cs
- PagesSection.cs
- HtmlCommandAdapter.cs
- CompositeDuplexElement.cs
- CmsInterop.cs
- WindowsAuthenticationModule.cs
- TripleDES.cs
- SchemaTypeEmitter.cs
- BooleanStorage.cs
- DataColumnPropertyDescriptor.cs
- Roles.cs
- DetailsViewDeletedEventArgs.cs
- ByteStreamGeometryContext.cs
- PrintPreviewGraphics.cs
- DynamicMetaObject.cs
- AuthenticodeSignatureInformation.cs
- CompilerGlobalScopeAttribute.cs
- MemoryStream.cs
- EntitySetDataBindingList.cs
- PolyLineSegment.cs
- GridErrorDlg.cs
- PropertyGrid.cs
- NativeMethods.cs
- GeometryHitTestResult.cs
- Color.cs
- DocumentViewerBaseAutomationPeer.cs
- DBNull.cs
- WebCategoryAttribute.cs
- HotSpotCollection.cs
- _Win32.cs
- RadialGradientBrush.cs
- GeneratedView.cs
- Roles.cs
- SqlAliaser.cs
- FontFamily.cs
- XmlResolver.cs
- Table.cs
- State.cs
- OleDbConnectionPoolGroupProviderInfo.cs