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