Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / CompoundFile / CompoundFileDeflateTransform.cs / 1 / CompoundFileDeflateTransform.cs
//------------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description:
// Implementation of a helper class that provides a fully functional Stream on unmanaged ZLib in a fashion
// consistent with Office and RMA (see Creating Rights-Managed HTML Files at
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rma/introduction.asp).
//
// History:
// 10/05/2005: BruceMac: First created.
// 02/14/2006: BruceMac: Rename file to reflect class name and apply security mitigations
// identified during security code review.
// 03/09/2006: BruceMac: Make AllocOrRealloc SecurityCritical because it allocates
// pinned memory based on caller arguments.
//-----------------------------------------------------------------------------
// Allow use of presharp warning numbers [6518] unknown to the compiler
#pragma warning disable 1634, 1691
using System;
using System.IO;
using System.Diagnostics;
using System.IO.Packaging;
using System.Windows;
using System.Runtime.InteropServices; // for Marshal class
using MS.Internal.IO.Packaging; // for PackagingUtilities
using System.Security; // for SecurityCritical and SecurityTreatAsSafe
namespace MS.Internal.IO.Packaging.CompoundFile
{
//-----------------------------------------------------
//
// Internal Members
//
//-----------------------------------------------------
///
/// Provides Office-compatible ZLib compression using interop to ZLib library
///
/// This class makes use of GCHandles in order to share data in a non-trivial fashion with the
/// unmanaged ZLib library. Because of this, it demands UnmanagedCodePermission of it's caller.
/// IDeflateTransform is a batch-oriented interface. All data will be transformed from source
/// to destination.
internal class CompoundFileDeflateTransform : IDeflateTransform
{
//------------------------------------------------------
//
// IDeflateTransform Interface
//
//-----------------------------------------------------
///
/// Decompress delegate - invoke ZLib in a manner consistent with RMA/Office
///
/// stream to read from
/// stream to write to
///
/// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments
/// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size.
/// AllocOrRealloc is used in two occasions:
/// 1. Compress - here we provide size based on our default block size (of 4k)
/// and growth will not exceed double this size (we are compressing, but sometimes
/// compression doesn't succeed in reducing sizes).
/// 2. Decompress - here the block size is based on values obtained from the
/// stream itself (see ReadBlockHeader) but we are careful to throw on malicious
/// input. Any size > 1MB is considered malicious and rejected.
///
[SecurityCritical, SecurityTreatAsSafe]
public void Decompress(Stream source, Stream sink)
{
if (source == null)
throw new ArgumentNullException("source");
if (sink == null)
throw new ArgumentNullException("sink");
Invariant.Assert(source.CanRead);
Invariant.Assert(sink.CanWrite, "Logic Error - Cannot decompress into a read-only stream");
// remember this for later
long storedPosition = -1;
try
{
if (source.CanSeek)
{
storedPosition = source.Position;
source.Position = 0;
}
if (sink.CanSeek)
sink.Position = 0;
// zlib state
UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream();
// initialize the zlib library
UnsafeNativeMethods.ZLib.ErrorCode retVal = 0;
retVal = UnsafeNativeMethods.ZLib.ums_inflate_init(ref zStream,
UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream));
ThrowIfZLibError(retVal);
byte[] sourceBuf = null; // source buffer
byte[] sinkBuf = null; // destination buffer - where to write data
GCHandle gcSourceBuf = new GCHandle(); // Preallocate these so we can safely access them
GCHandle gcSinkBuf = new GCHandle(); // in the next finally block.
try
{
// read all available data
// each block is preceded by a header that is 3 ulongs
int uncompressedSize, compressedSize;
long destStreamLength = 0; // keep track of decompressed size
while (ReadBlockHeader(source, out uncompressedSize, out compressedSize))
{
// ensure we have space
AllocOrRealloc(compressedSize, ref sourceBuf, ref gcSourceBuf);
AllocOrRealloc(uncompressedSize, ref sinkBuf, ref gcSinkBuf);
// read the data into the sourceBuf
int bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, compressedSize);
if (bytesRead > 0)
{
if (compressedSize != bytesRead)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// prepare structure
// The buffer pointers must be reset for every call
// because ums_inflate modifies them
zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject();
zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject();
zStream.cbIn = (uint)bytesRead; // this is number of bytes available for decompression at pInBuf and is updated by ums_deflate call
zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call
// InvokeZLib does the actual interop. It updates zStream, and sinkBuf (sourceBuf passed by ref to avoid copying)
// and leaves the decompressed data in sinkBuf.
// int decompressedSize = InvokeZLib(bytesRead, ref zStream, ref sourceBuf, ref sinkBuf, pSource, pSink, false);
retVal = UnsafeNativeMethods.ZLib.ums_inflate(ref zStream,
(int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush);
ThrowIfZLibError(retVal);
checked
{
int decompressedSize = sinkBuf.Length - (int)zStream.cbOut;
// verify that data matches header
if (decompressedSize != uncompressedSize)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
destStreamLength += decompressedSize;
// write to the base stream
sink.Write(sinkBuf, 0, decompressedSize);
}
}
else
{
// block header but no block data
if (compressedSize != 0)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
}
// make sure we truncate if the destination stream was longer than this current decompress
if (sink.CanSeek)
sink.SetLength(destStreamLength);
}
finally
{
if (gcSourceBuf.IsAllocated)
gcSourceBuf.Free();
if (gcSinkBuf.IsAllocated)
gcSinkBuf.Free();
}
}
finally
{
// seek to the current logical position before returning
if (source.CanSeek)
source.Position = storedPosition;
}
}
///
/// Compress delegate - invoke ZLib in a manner consistent with RMA/Office
///
///
///
/// We are careful to avoid use of Position, Length or SetLength on non-seekable streams. If
/// source or sink are non-seekable, it is assumed that positions are correctly set upon entry and that
/// they need not be restored. We also assume that destination stream length need not be truncated.
///
/// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments
/// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size.
/// AllocOrRealloc is used in two occasions:
/// 1. Compress - here we provide size based on our default block size (of 4k)
/// and growth will not exceed double this size (we are compressing, but sometimes
/// compression doesn't succeed in reducing sizes).
/// 2. Decompress - here the block size is based on values obtained from the
/// stream itself (see ReadBlockHeader) but we are careful to throw on malicious
/// input. Any size > 1MB is considered malicious and rejected.
///
[SecurityCritical, SecurityTreatAsSafe]
public void Compress(Stream source, Stream sink)
{
if (source == null)
throw new ArgumentNullException("source");
if (sink == null)
throw new ArgumentNullException("sink");
Invariant.Assert(source.CanRead);
Invariant.Assert(sink.CanWrite, "Logic Error - Cannot compress into a read-only stream");
// remember this for later if possible
long storedPosition = -1; // default to illegal value to catch any logic errors
try
{
int sourceBufferSize; // don't allocate 4k for really tiny source streams
if (source.CanSeek)
{
storedPosition = source.Position;
source.Position = 0;
// Casting result to int is safe because _defaultBlockSize is very small and the result
// of Math.Min(x, _defaultBlockSize) must be no larger than _defaultBlockSize.
sourceBufferSize = (int)(Math.Min(source.Length, (long)_defaultBlockSize));
}
else
sourceBufferSize = _defaultBlockSize; // can't call Length so fallback to default
if (sink.CanSeek)
sink.Position = 0;
// zlib state
UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream();
// initialize the zlib library
UnsafeNativeMethods.ZLib.ErrorCode retVal = UnsafeNativeMethods.ZLib.ums_deflate_init(ref zStream, _compressionLevel,
UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream));
ThrowIfZLibError(retVal);
// where to write data - can actually grow if data is uncompressible
long destStreamLength = 0;
byte[] sourceBuf = null; // source buffer
byte[] sinkBuf = null; // destination buffer
GCHandle gcSourceBuf = new GCHandle();
GCHandle gcSinkBuf = new GCHandle();
try
{
// allocate managed buffers
AllocOrRealloc(sourceBufferSize, ref sourceBuf, ref gcSourceBuf);
AllocOrRealloc(_defaultBlockSize + (_defaultBlockSize >> 1), ref sinkBuf, ref gcSinkBuf);
// while (more data is available)
// - read into the sourceBuf
// - compress into the sinkBuf
// - emit the header
// - write out to the _baseStream
// Suppress 6518 Local IDisposable object not disposed:
// Reason: The stream is not owned by us, therefore we cannot
// close the BinaryWriter as it will Close the stream underneath.
#pragma warning disable 6518
BinaryWriter writer = new BinaryWriter(sink);
int bytesRead;
while ((bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, sourceBuf.Length)) > 0)
{
Invariant.Assert(bytesRead <= sourceBufferSize);
// prepare structure
// these pointers must be re-assigned for each loop because
// ums_deflate modifies them
zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject();
zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject();
zStream.cbIn = (uint)bytesRead; // this is number of bytes available for compression at pInBuf and is updated by ums_deflate call
zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call
// cast is safe because SyncFlush is a constant
retVal = UnsafeNativeMethods.ZLib.ums_deflate(ref zStream,
(int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush);
ThrowIfZLibError(retVal);
checked
{
int compressedSize = sinkBuf.Length - (int)zStream.cbOut;
Invariant.Assert(compressedSize > 0, "compressing non-zero bytes creates a non-empty block");
// This should never happen because our destination buffer
// is twice as large as our source buffer
Invariant.Assert(zStream.cbIn == 0, "Expecting all data to be compressed!");
// write the header
writer.Write(_blockHeaderToken); // token
writer.Write((UInt32)bytesRead);
writer.Write((UInt32)compressedSize);
destStreamLength += _headerBuf.Length;
// write to the base stream
sink.Write(sinkBuf, 0, compressedSize);
destStreamLength += compressedSize;
}
}
// post-compression
// truncate if necessary
if (sink.CanSeek)
sink.SetLength(destStreamLength);
}
finally
{
if (gcSourceBuf.IsAllocated)
gcSourceBuf.Free();
if (gcSinkBuf.IsAllocated)
gcSinkBuf.Free();
}
#pragma warning restore 6518
}
finally
{
// seek to the current logical position before returning
if (sink.CanSeek)
source.Position = storedPosition;
}
}
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
///
/// Ensures that Buffer has enough room for size using alloc or realloc
///
/// buffer - may be null
/// desired size
/// handle
/// When this exits, buffer is at least as large as size
/// and gcHandle is pointing to the pinned buffer. If the buffer was already large enough,
/// no action is taken.
///
/// Critical - allocates pinned memory based on arguments
///
[SecurityCritical]
private static void AllocOrRealloc(int size, ref byte[] buffer, ref GCHandle gcHandle)
{
Invariant.Assert(size >= 0, "Cannot allocate negative number of bytes");
// verify we have room
if (buffer != null)
{
// do we have room?
if (buffer.Length < size)
{
// overallocate to reduce the chance of future reallocations
size = Math.Max(size, buffer.Length + (buffer.Length >> 1)); // fast Length * 1.5
// free existing because it's too small
if (gcHandle.IsAllocated)
gcHandle.Free();
}
else
return; // current buffer satisfies the request so there is no need to alloc
}
// We have to allocate in two cases:
// 1. We were called with buffer == null
// 2. The original buffer was too small
buffer = new byte[size]; // managed source buffer
gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); // pinned so unmanaged code can read/write it
}
///
/// ReadBlockHeader - reads the block header and returns true if successful
///
/// stream to read from
/// compressedSize from header
/// uncompressedSize from header
/// true if header found
private bool ReadBlockHeader(Stream source,
out int uncompressedSize, out int compressedSize)
{
int bytesRead = PackagingUtilities.ReliableRead(source, _headerBuf, 0, _headerBuf.Length);
if (bytesRead > 0)
{
if (bytesRead < _headerBuf.Length)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// header format = 3 ulong's
// read and inspect token
uint token = BitConverter.ToUInt32(_headerBuf, _ulongSize * 0);
if (token != _blockHeaderToken)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// convert to int's as that's what we use everywhere
checked
{
uncompressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 1);
compressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 2);
// screen out malicious data
if (uncompressedSize < 0 || uncompressedSize > _maxAllowableBlockSize
|| compressedSize < 0 || compressedSize > _maxAllowableBlockSize)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
}
else
{
uncompressedSize = compressedSize = 0;
}
return (bytesRead > 0);
}
///
/// Throw exception based on ZLib error code
///
///
private static void ThrowIfZLibError(UnsafeNativeMethods.ZLib.ErrorCode retVal)
{
// switch does not support fall-through
bool invalidOperation = false;
bool corruption = false;
switch (retVal)
{
case UnsafeNativeMethods.ZLib.ErrorCode.Success:
return;
case UnsafeNativeMethods.ZLib.ErrorCode.StreamEnd:
invalidOperation = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.NeedDictionary:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.StreamError:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.DataError:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.MemError:
throw new OutOfMemoryException();
case UnsafeNativeMethods.ZLib.ErrorCode.BufError:
invalidOperation = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.VersionError:
throw new InvalidOperationException(SR.Get(SRID.ZLibVersionError,
UnsafeNativeMethods.ZLib.ZLibVersion));
default:
{
// ErrorNo
throw new IOException();
}
}
if (invalidOperation)
throw new InvalidOperationException();
if (corruption)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
// for reading each block header
private byte[] _headerBuf = new byte[_blockHeaderSize]; // 3 ulongs
// static
private const int _defaultBlockSize = 0x1000; // 4k default
private const int _maxAllowableBlockSize = 0xFFFFF; // The spec is open ended about supported block sizes but we
// want to defend against malicious input so we restrict input to 1MB.
private const int _compressionLevel = 9; // mandated by format
private const int _ulongSize = 4; // a ULONG in unmanaged C++ is 4 bytes
private const UInt32 _blockHeaderToken = 0x0FA0; // signature at start of each block header
private const int _blockHeaderSize = _ulongSize * 3; // length of block header
}
}
// 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:
// Implementation of a helper class that provides a fully functional Stream on unmanaged ZLib in a fashion
// consistent with Office and RMA (see Creating Rights-Managed HTML Files at
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rma/introduction.asp).
//
// History:
// 10/05/2005: BruceMac: First created.
// 02/14/2006: BruceMac: Rename file to reflect class name and apply security mitigations
// identified during security code review.
// 03/09/2006: BruceMac: Make AllocOrRealloc SecurityCritical because it allocates
// pinned memory based on caller arguments.
//-----------------------------------------------------------------------------
// Allow use of presharp warning numbers [6518] unknown to the compiler
#pragma warning disable 1634, 1691
using System;
using System.IO;
using System.Diagnostics;
using System.IO.Packaging;
using System.Windows;
using System.Runtime.InteropServices; // for Marshal class
using MS.Internal.IO.Packaging; // for PackagingUtilities
using System.Security; // for SecurityCritical and SecurityTreatAsSafe
namespace MS.Internal.IO.Packaging.CompoundFile
{
//-----------------------------------------------------
//
// Internal Members
//
//-----------------------------------------------------
///
/// Provides Office-compatible ZLib compression using interop to ZLib library
///
/// This class makes use of GCHandles in order to share data in a non-trivial fashion with the
/// unmanaged ZLib library. Because of this, it demands UnmanagedCodePermission of it's caller.
/// IDeflateTransform is a batch-oriented interface. All data will be transformed from source
/// to destination.
internal class CompoundFileDeflateTransform : IDeflateTransform
{
//------------------------------------------------------
//
// IDeflateTransform Interface
//
//-----------------------------------------------------
///
/// Decompress delegate - invoke ZLib in a manner consistent with RMA/Office
///
/// stream to read from
/// stream to write to
///
/// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments
/// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size.
/// AllocOrRealloc is used in two occasions:
/// 1. Compress - here we provide size based on our default block size (of 4k)
/// and growth will not exceed double this size (we are compressing, but sometimes
/// compression doesn't succeed in reducing sizes).
/// 2. Decompress - here the block size is based on values obtained from the
/// stream itself (see ReadBlockHeader) but we are careful to throw on malicious
/// input. Any size > 1MB is considered malicious and rejected.
///
[SecurityCritical, SecurityTreatAsSafe]
public void Decompress(Stream source, Stream sink)
{
if (source == null)
throw new ArgumentNullException("source");
if (sink == null)
throw new ArgumentNullException("sink");
Invariant.Assert(source.CanRead);
Invariant.Assert(sink.CanWrite, "Logic Error - Cannot decompress into a read-only stream");
// remember this for later
long storedPosition = -1;
try
{
if (source.CanSeek)
{
storedPosition = source.Position;
source.Position = 0;
}
if (sink.CanSeek)
sink.Position = 0;
// zlib state
UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream();
// initialize the zlib library
UnsafeNativeMethods.ZLib.ErrorCode retVal = 0;
retVal = UnsafeNativeMethods.ZLib.ums_inflate_init(ref zStream,
UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream));
ThrowIfZLibError(retVal);
byte[] sourceBuf = null; // source buffer
byte[] sinkBuf = null; // destination buffer - where to write data
GCHandle gcSourceBuf = new GCHandle(); // Preallocate these so we can safely access them
GCHandle gcSinkBuf = new GCHandle(); // in the next finally block.
try
{
// read all available data
// each block is preceded by a header that is 3 ulongs
int uncompressedSize, compressedSize;
long destStreamLength = 0; // keep track of decompressed size
while (ReadBlockHeader(source, out uncompressedSize, out compressedSize))
{
// ensure we have space
AllocOrRealloc(compressedSize, ref sourceBuf, ref gcSourceBuf);
AllocOrRealloc(uncompressedSize, ref sinkBuf, ref gcSinkBuf);
// read the data into the sourceBuf
int bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, compressedSize);
if (bytesRead > 0)
{
if (compressedSize != bytesRead)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// prepare structure
// The buffer pointers must be reset for every call
// because ums_inflate modifies them
zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject();
zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject();
zStream.cbIn = (uint)bytesRead; // this is number of bytes available for decompression at pInBuf and is updated by ums_deflate call
zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call
// InvokeZLib does the actual interop. It updates zStream, and sinkBuf (sourceBuf passed by ref to avoid copying)
// and leaves the decompressed data in sinkBuf.
// int decompressedSize = InvokeZLib(bytesRead, ref zStream, ref sourceBuf, ref sinkBuf, pSource, pSink, false);
retVal = UnsafeNativeMethods.ZLib.ums_inflate(ref zStream,
(int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush);
ThrowIfZLibError(retVal);
checked
{
int decompressedSize = sinkBuf.Length - (int)zStream.cbOut;
// verify that data matches header
if (decompressedSize != uncompressedSize)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
destStreamLength += decompressedSize;
// write to the base stream
sink.Write(sinkBuf, 0, decompressedSize);
}
}
else
{
// block header but no block data
if (compressedSize != 0)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
}
// make sure we truncate if the destination stream was longer than this current decompress
if (sink.CanSeek)
sink.SetLength(destStreamLength);
}
finally
{
if (gcSourceBuf.IsAllocated)
gcSourceBuf.Free();
if (gcSinkBuf.IsAllocated)
gcSinkBuf.Free();
}
}
finally
{
// seek to the current logical position before returning
if (source.CanSeek)
source.Position = storedPosition;
}
}
///
/// Compress delegate - invoke ZLib in a manner consistent with RMA/Office
///
///
///
/// We are careful to avoid use of Position, Length or SetLength on non-seekable streams. If
/// source or sink are non-seekable, it is assumed that positions are correctly set upon entry and that
/// they need not be restored. We also assume that destination stream length need not be truncated.
///
/// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments
/// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size.
/// AllocOrRealloc is used in two occasions:
/// 1. Compress - here we provide size based on our default block size (of 4k)
/// and growth will not exceed double this size (we are compressing, but sometimes
/// compression doesn't succeed in reducing sizes).
/// 2. Decompress - here the block size is based on values obtained from the
/// stream itself (see ReadBlockHeader) but we are careful to throw on malicious
/// input. Any size > 1MB is considered malicious and rejected.
///
[SecurityCritical, SecurityTreatAsSafe]
public void Compress(Stream source, Stream sink)
{
if (source == null)
throw new ArgumentNullException("source");
if (sink == null)
throw new ArgumentNullException("sink");
Invariant.Assert(source.CanRead);
Invariant.Assert(sink.CanWrite, "Logic Error - Cannot compress into a read-only stream");
// remember this for later if possible
long storedPosition = -1; // default to illegal value to catch any logic errors
try
{
int sourceBufferSize; // don't allocate 4k for really tiny source streams
if (source.CanSeek)
{
storedPosition = source.Position;
source.Position = 0;
// Casting result to int is safe because _defaultBlockSize is very small and the result
// of Math.Min(x, _defaultBlockSize) must be no larger than _defaultBlockSize.
sourceBufferSize = (int)(Math.Min(source.Length, (long)_defaultBlockSize));
}
else
sourceBufferSize = _defaultBlockSize; // can't call Length so fallback to default
if (sink.CanSeek)
sink.Position = 0;
// zlib state
UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream();
// initialize the zlib library
UnsafeNativeMethods.ZLib.ErrorCode retVal = UnsafeNativeMethods.ZLib.ums_deflate_init(ref zStream, _compressionLevel,
UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream));
ThrowIfZLibError(retVal);
// where to write data - can actually grow if data is uncompressible
long destStreamLength = 0;
byte[] sourceBuf = null; // source buffer
byte[] sinkBuf = null; // destination buffer
GCHandle gcSourceBuf = new GCHandle();
GCHandle gcSinkBuf = new GCHandle();
try
{
// allocate managed buffers
AllocOrRealloc(sourceBufferSize, ref sourceBuf, ref gcSourceBuf);
AllocOrRealloc(_defaultBlockSize + (_defaultBlockSize >> 1), ref sinkBuf, ref gcSinkBuf);
// while (more data is available)
// - read into the sourceBuf
// - compress into the sinkBuf
// - emit the header
// - write out to the _baseStream
// Suppress 6518 Local IDisposable object not disposed:
// Reason: The stream is not owned by us, therefore we cannot
// close the BinaryWriter as it will Close the stream underneath.
#pragma warning disable 6518
BinaryWriter writer = new BinaryWriter(sink);
int bytesRead;
while ((bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, sourceBuf.Length)) > 0)
{
Invariant.Assert(bytesRead <= sourceBufferSize);
// prepare structure
// these pointers must be re-assigned for each loop because
// ums_deflate modifies them
zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject();
zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject();
zStream.cbIn = (uint)bytesRead; // this is number of bytes available for compression at pInBuf and is updated by ums_deflate call
zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call
// cast is safe because SyncFlush is a constant
retVal = UnsafeNativeMethods.ZLib.ums_deflate(ref zStream,
(int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush);
ThrowIfZLibError(retVal);
checked
{
int compressedSize = sinkBuf.Length - (int)zStream.cbOut;
Invariant.Assert(compressedSize > 0, "compressing non-zero bytes creates a non-empty block");
// This should never happen because our destination buffer
// is twice as large as our source buffer
Invariant.Assert(zStream.cbIn == 0, "Expecting all data to be compressed!");
// write the header
writer.Write(_blockHeaderToken); // token
writer.Write((UInt32)bytesRead);
writer.Write((UInt32)compressedSize);
destStreamLength += _headerBuf.Length;
// write to the base stream
sink.Write(sinkBuf, 0, compressedSize);
destStreamLength += compressedSize;
}
}
// post-compression
// truncate if necessary
if (sink.CanSeek)
sink.SetLength(destStreamLength);
}
finally
{
if (gcSourceBuf.IsAllocated)
gcSourceBuf.Free();
if (gcSinkBuf.IsAllocated)
gcSinkBuf.Free();
}
#pragma warning restore 6518
}
finally
{
// seek to the current logical position before returning
if (sink.CanSeek)
source.Position = storedPosition;
}
}
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
///
/// Ensures that Buffer has enough room for size using alloc or realloc
///
/// buffer - may be null
/// desired size
/// handle
/// When this exits, buffer is at least as large as size
/// and gcHandle is pointing to the pinned buffer. If the buffer was already large enough,
/// no action is taken.
///
/// Critical - allocates pinned memory based on arguments
///
[SecurityCritical]
private static void AllocOrRealloc(int size, ref byte[] buffer, ref GCHandle gcHandle)
{
Invariant.Assert(size >= 0, "Cannot allocate negative number of bytes");
// verify we have room
if (buffer != null)
{
// do we have room?
if (buffer.Length < size)
{
// overallocate to reduce the chance of future reallocations
size = Math.Max(size, buffer.Length + (buffer.Length >> 1)); // fast Length * 1.5
// free existing because it's too small
if (gcHandle.IsAllocated)
gcHandle.Free();
}
else
return; // current buffer satisfies the request so there is no need to alloc
}
// We have to allocate in two cases:
// 1. We were called with buffer == null
// 2. The original buffer was too small
buffer = new byte[size]; // managed source buffer
gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); // pinned so unmanaged code can read/write it
}
///
/// ReadBlockHeader - reads the block header and returns true if successful
///
/// stream to read from
/// compressedSize from header
/// uncompressedSize from header
/// true if header found
private bool ReadBlockHeader(Stream source,
out int uncompressedSize, out int compressedSize)
{
int bytesRead = PackagingUtilities.ReliableRead(source, _headerBuf, 0, _headerBuf.Length);
if (bytesRead > 0)
{
if (bytesRead < _headerBuf.Length)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// header format = 3 ulong's
// read and inspect token
uint token = BitConverter.ToUInt32(_headerBuf, _ulongSize * 0);
if (token != _blockHeaderToken)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
// convert to int's as that's what we use everywhere
checked
{
uncompressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 1);
compressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 2);
// screen out malicious data
if (uncompressedSize < 0 || uncompressedSize > _maxAllowableBlockSize
|| compressedSize < 0 || compressedSize > _maxAllowableBlockSize)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
}
else
{
uncompressedSize = compressedSize = 0;
}
return (bytesRead > 0);
}
///
/// Throw exception based on ZLib error code
///
///
private static void ThrowIfZLibError(UnsafeNativeMethods.ZLib.ErrorCode retVal)
{
// switch does not support fall-through
bool invalidOperation = false;
bool corruption = false;
switch (retVal)
{
case UnsafeNativeMethods.ZLib.ErrorCode.Success:
return;
case UnsafeNativeMethods.ZLib.ErrorCode.StreamEnd:
invalidOperation = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.NeedDictionary:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.StreamError:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.DataError:
corruption = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.MemError:
throw new OutOfMemoryException();
case UnsafeNativeMethods.ZLib.ErrorCode.BufError:
invalidOperation = true; break;
case UnsafeNativeMethods.ZLib.ErrorCode.VersionError:
throw new InvalidOperationException(SR.Get(SRID.ZLibVersionError,
UnsafeNativeMethods.ZLib.ZLibVersion));
default:
{
// ErrorNo
throw new IOException();
}
}
if (invalidOperation)
throw new InvalidOperationException();
if (corruption)
throw new FileFormatException(SR.Get(SRID.CorruptStream));
}
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
// for reading each block header
private byte[] _headerBuf = new byte[_blockHeaderSize]; // 3 ulongs
// static
private const int _defaultBlockSize = 0x1000; // 4k default
private const int _maxAllowableBlockSize = 0xFFFFF; // The spec is open ended about supported block sizes but we
// want to defend against malicious input so we restrict input to 1MB.
private const int _compressionLevel = 9; // mandated by format
private const int _ulongSize = 4; // a ULONG in unmanaged C++ is 4 bytes
private const UInt32 _blockHeaderToken = 0x0FA0; // signature at start of each block header
private const int _blockHeaderSize = _ulongSize * 3; // length of block header
}
}
// 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
- ProcessDesigner.cs
- GroupItemAutomationPeer.cs
- MessageQueueCriteria.cs
- Assert.cs
- FilterQueryOptionExpression.cs
- XamlHostingSection.cs
- DiagnosticTraceSchemas.cs
- Size3D.cs
- ListBoxItemWrapperAutomationPeer.cs
- BorderGapMaskConverter.cs
- ItemsControl.cs
- shaper.cs
- DispatcherEventArgs.cs
- DesignerDataRelationship.cs
- AlternateViewCollection.cs
- IDReferencePropertyAttribute.cs
- GenericAuthenticationEventArgs.cs
- OrderedDictionaryStateHelper.cs
- FormsAuthenticationConfiguration.cs
- DbDataAdapter.cs
- Aggregates.cs
- Enum.cs
- CompleteWizardStep.cs
- BrowserCapabilitiesFactory35.cs
- FrameworkTextComposition.cs
- OutputCacheModule.cs
- ConfigurationElement.cs
- MSHTMLHostUtil.cs
- Attribute.cs
- assemblycache.cs
- InputBuffer.cs
- BmpBitmapEncoder.cs
- XmlRawWriter.cs
- SerializationStore.cs
- AttributeEmitter.cs
- OdbcInfoMessageEvent.cs
- PriorityQueue.cs
- ScalarType.cs
- ListBoxItemAutomationPeer.cs
- CombinedGeometry.cs
- ThousandthOfEmRealDoubles.cs
- _RequestCacheProtocol.cs
- XsltInput.cs
- LoadedOrUnloadedOperation.cs
- dataobject.cs
- GregorianCalendar.cs
- FlowDocumentFormatter.cs
- EditorPartChrome.cs
- XmlnsCache.cs
- QueryResponse.cs
- DetailsView.cs
- StatusBarDrawItemEvent.cs
- CreateRefExpr.cs
- AtomEntry.cs
- Dispatcher.cs
- ObfuscationAttribute.cs
- EventNotify.cs
- HtmlGenericControl.cs
- RegexStringValidatorAttribute.cs
- XmlEntityReference.cs
- EFAssociationProvider.cs
- TemplateControl.cs
- NumericExpr.cs
- DataSysAttribute.cs
- SafeCryptContextHandle.cs
- FlowDocumentReader.cs
- TextProviderWrapper.cs
- Event.cs
- PageTrueTypeFont.cs
- CommandExpr.cs
- CodeSnippetTypeMember.cs
- RemoteWebConfigurationHostStream.cs
- SafeArrayRankMismatchException.cs
- ListViewItemMouseHoverEvent.cs
- DtcInterfaces.cs
- MenuItemBindingCollection.cs
- PositiveTimeSpanValidatorAttribute.cs
- DataGridViewComboBoxColumn.cs
- TextTreeRootNode.cs
- DetailsViewDeletedEventArgs.cs
- XmlILModule.cs
- CodeCatchClauseCollection.cs
- CompiledRegexRunnerFactory.cs
- EventWaitHandleSecurity.cs
- mediaeventargs.cs
- CombinedGeometry.cs
- XmlUtf8RawTextWriter.cs
- EmulateRecognizeCompletedEventArgs.cs
- TemplateKey.cs
- MatrixConverter.cs
- TdsParserHelperClasses.cs
- AnchoredBlock.cs
- VerificationAttribute.cs
- BezierSegment.cs
- ParallelSeparator.xaml.cs
- Geometry.cs
- WindowsListViewItem.cs
- RadioButtonFlatAdapter.cs
- SafeLocalMemHandle.cs
- PropertyMetadata.cs