public int Inflate(byte[] bytes, int offset, int length) { Debug.Assert(null != bytes, "Can't pass in a null output buffer!"); // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes // have been read if (NeedsInput() || _inputBufferHandle == null || !_inputBufferHandle.IsAllocated || length == 0) { return(0); } // State is valid; attempt inflation try { int bytesRead; ZLibNative.ErrorCode errc = ReadInflateOutput(bytes, offset, length, ZLibNative.FlushCode.NoFlush, out bytesRead); if (errc == ZLibNative.ErrorCode.StreamEnd) { _finished = true; } return(bytesRead); } finally { // Before returning, make sure to release input buffer if necesary: if (0 == _zlibStream.AvailIn && _inputBufferHandle.IsAllocated) { DeallocateInputBufferHandle(); } } }
/// <summary> /// Initializes a new ZLibException with serialized data. /// </summary> /// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param> /// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param> protected ZLibException(SerializationInfo info, StreamingContext context) : base(info, context) { string errContext = info.GetString("zlibErrorContext"); ZLibNative.ErrorCode errCode = (ZLibNative.ErrorCode)info.GetInt32("zlibErrorCode"); string errMessage = info.GetString("zlibErrorMessage"); Init(errContext, errCode, errMessage); }
/// <summary> /// Wrapper around the ZLib inflate function, configuring the stream appropriately. /// </summary> private unsafe ZLibNative.ErrorCode ReadInflateOutput(IntPtr bufPtr, int length, ZLibNative.FlushCode flushCode, out int bytesRead) { lock (_syncLock) { _zlibStream.NextOut = bufPtr; _zlibStream.AvailOut = (uint)length; ZLibNative.ErrorCode errC = Inflate(flushCode); bytesRead = length - (int)_zlibStream.AvailOut; return(errC); } }
/// <summary> /// Throw exception based on ZLib error code /// </summary> /// <param name="retVal"></param> private static void ThrowIfZLibError(ZLibNative.ErrorCode retVal) { // switch does not support fall-through bool invalidOperation = false; bool corruption = false; switch (retVal) { case ZLibNative.ErrorCode.Ok: return; case ZLibNative.ErrorCode.StreamEnd: invalidOperation = true; break; case ZLibNative.ErrorCode.NeedDictionary: corruption = true; break; case ZLibNative.ErrorCode.StreamError: corruption = true; break; case ZLibNative.ErrorCode.DataError: corruption = true; break; case ZLibNative.ErrorCode.MemError: throw new OutOfMemoryException(); case ZLibNative.ErrorCode.BufError: invalidOperation = true; break; case ZLibNative.ErrorCode.VersionError: throw new InvalidOperationException(SR.Get(SRID.ZLibVersionError, System.Text.Encoding.UTF8.GetString(ZLibVersion, 0, ZLibVersion.Length))); default: { // ErrorNo throw new IOException(); } } if (invalidOperation) { throw new InvalidOperationException(); } if (corruption) { throw new FileFormatException(SR.Get(SRID.CorruptStream)); } }
/// <summary> /// Translates the given byte array to a GCHandle so that it can be passed to the ZLib /// Inflate function, then returns the result of that call. /// </summary> private unsafe ZLibNative.ErrorCode ReadInflateOutput(byte[] outputBuffer, int offset, int length, ZLibNative.FlushCode flushCode, out int bytesRead) { lock (_syncLock) { fixed(byte *bufPtr = outputBuffer) { _zlibStream.NextOut = (IntPtr)bufPtr + offset; _zlibStream.AvailOut = (uint)length; ZLibNative.ErrorCode errC = Inflate(flushCode); bytesRead = length - (int)_zlibStream.AvailOut; return(errC); } } }
/// <summary> /// This is the preferred constructor to use. /// The other constructors are provided for compliance to Fx design guidelines. /// </summary> /// <param name="message">A (localised) human readable error description.</param> /// <param name="zlibErrorContext">A description of the context within zlib where the error occurred (e.g. the function name).</param> /// <param name="zlibErrorCode">The error code returned by a ZLib function that caused this exception.</param> /// <param name="zlibErrorMessage">The string provided by ZLib as error information (unlocalised).</param> public ZLibException(string?message, string?zlibErrorContext, int zlibErrorCode, string?zlibErrorMessage) : base(message) { _zlibErrorContext = zlibErrorContext; _zlibErrorCode = (ZLibNative.ErrorCode)zlibErrorCode; _zlibErrorMessage = zlibErrorMessage; }
//------------------------------------------------------ // // IDeflateTransform Interface // //------------------------------------------------------ /// <summary> /// Decompress delegate - invoke ZLib in a manner consistent with RMA/Office /// </summary> /// <param name="source">stream to read from</param> /// <param name="sink">stream to write to</param> 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 ZLibNative.ZLibStreamHandle zStream; // initialize the zlib library ZLibNative.ErrorCode retVal = ZLibNative.CreateZLibStreamForInflate(out zStream, DEFAULT_WINDOW_BITS); 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 ZLibNative.Inflate modifies them zStream.NextIn = gcSourceBuf.AddrOfPinnedObject(); zStream.NextOut = gcSinkBuf.AddrOfPinnedObject(); zStream.AvailIn = (uint)bytesRead; // this is number of bytes available for decompression at pInBuf and is updated by ums_deflate call zStream.AvailOut = (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 = zStream.Inflate(ZLibNative.FlushCode.SyncFlush); ThrowIfZLibError(retVal); checked { int decompressedSize = sinkBuf.Length - (int)zStream.AvailOut; // 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; } } }
/// <summary> /// Compress delegate - invoke ZLib in a manner consistent with RMA/Office /// </summary> /// <param name="source"></param> /// <param name="sink"></param> /// <remarks>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.</remarks> 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 ZLibNative.ZLibStreamHandle zStream; // initialize the zlib library ZLibNative.ErrorCode retVal = ZLibNative.CreateZLibStreamForDeflate( out zStream, ZLibNative.CompressionLevel.DefaultCompression, DEFAULT_WINDOW_BITS, DEFAULT_MEM_LEVEL, ZLibNative.CompressionStrategy.DefaultStrategy); 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.NextIn = gcSourceBuf.AddrOfPinnedObject(); zStream.NextOut = gcSinkBuf.AddrOfPinnedObject(); zStream.AvailIn = (uint)bytesRead; // this is number of bytes available for compression at pInBuf and is updated by ums_deflate call zStream.AvailOut = (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 = zStream.Deflate(ZLibNative.FlushCode.SyncFlush); ThrowIfZLibError(retVal); checked { int compressedSize = sinkBuf.Length - (int)zStream.AvailOut; 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.AvailIn == 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; } } }