// InflaterInputStream.cs // // Copyright (C) 2001 Mike Krueger // Copyright (C) 2004 John Reilly // // This file was translated from java, it was part of the GNU Classpath // Copyright (C) 2001 Free Software Foundation, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give you // permission to link this library with independent modules to produce an // executable, regardless of the license terms of these independent // modules, and to copy and distribute the resulting executable under // terms of your choice, provided that you also meet, for each linked // independent module, the terms and conditions of the license of that // module. An independent module is a module which is not derived from // or based on this library. If you modify this library, you may extend // this exception to your version of the library, but you are not // obligated to do so. If you do not wish to do so, delete this // exception statement from your version. // HISTORY // 11-08-2009 GeoffHart T9121 Added Multi-member gzip support using System; using System.IO; // ReSharper disable RedundantThisQualifier #if false//!NETCF_1_0 using System.Security.Cryptography; #endif namespace PdfSharp.SharpZipLib.Zip.Compression.Streams { /// /// An input buffer customised for use by /// /// /// The buffer supports decryption of incoming data. /// internal class InflaterInputBuffer { #region Constructors /// /// Initialise a new instance of with a default buffer size /// /// The stream to buffer. public InflaterInputBuffer(Stream stream) : this(stream, 4096) { } /// /// Initialise a new instance of /// /// The stream to buffer. /// The size to use for the buffer /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. public InflaterInputBuffer(Stream stream, int bufferSize) { inputStream = stream; if (bufferSize < 1024) { bufferSize = 1024; } rawData = new byte[bufferSize]; clearText = rawData; } #endregion /// /// Get the length of bytes bytes in the /// public int RawLength { get { return rawLength; } } /// /// Get the contents of the raw data buffer. /// /// This may contain encrypted data. public byte[] RawData { get { return rawData; } } /// /// Get the number of useable bytes in /// public int ClearTextLength { get { return clearTextLength; } } /// /// Get the contents of the clear text buffer. /// public byte[] ClearText { get { return clearText; } } /// /// Get/set the number of bytes available /// public int Available { get { return available; } set { available = value; } } /// /// Call passing the current clear text buffer contents. /// /// The inflater to set input for. public void SetInflaterInput(Inflater inflater) { if (available > 0) { inflater.SetInput(clearText, clearTextLength - available, available); available = 0; } } /// /// Fill the buffer from the underlying input stream. /// public void Fill() { rawLength = 0; int toRead = rawData.Length; while (toRead > 0) { int count = inputStream.Read(rawData, rawLength, toRead); if (count <= 0) { break; } rawLength += count; toRead -= count; } #if false//!NETCF_1_0 if ( cryptoTransform != null ) { clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); } else #endif { clearTextLength = rawLength; } available = clearTextLength; } /// /// Read a buffer directly from the input stream /// /// The buffer to fill /// Returns the number of bytes read. public int ReadRawBuffer(byte[] buffer) { return ReadRawBuffer(buffer, 0, buffer.Length); } /// /// Read a buffer directly from the input stream /// /// The buffer to read into /// The offset to start reading data into. /// The number of bytes to read. /// Returns the number of bytes read. public int ReadRawBuffer(byte[] outBuffer, int offset, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } int currentOffset = offset; int currentLength = length; while (currentLength > 0) { if (available <= 0) { Fill(); if (available <= 0) { return 0; } } int toCopy = Math.Min(currentLength, available); System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); currentOffset += toCopy; currentLength -= toCopy; available -= toCopy; } return length; } /// /// Read clear text data from the input stream. /// /// The buffer to add data to. /// The offset to start adding data at. /// The number of bytes to read. /// Returns the number of bytes actually read. public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } int currentOffset = offset; int currentLength = length; while (currentLength > 0) { if (available <= 0) { Fill(); if (available <= 0) { return 0; } } int toCopy = Math.Min(currentLength, available); Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy); currentOffset += toCopy; currentLength -= toCopy; available -= toCopy; } return length; } /// /// Read a from the input stream. /// /// Returns the byte read. public int ReadLeByte() { if (available <= 0) { Fill(); if (available <= 0) { throw new ZipException("EOF in header"); } } byte result = rawData[rawLength - available]; available -= 1; return result; } /// /// Read an in little endian byte order. /// /// The short value read case to an int. public int ReadLeShort() { return ReadLeByte() | (ReadLeByte() << 8); } /// /// Read an in little endian byte order. /// /// The int value read. public int ReadLeInt() { return ReadLeShort() | (ReadLeShort() << 16); } /// /// Read a in little endian byte order. /// /// The long value read. public long ReadLeLong() { return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); } #if false//!NETCF_1_0 /// /// Get/set the to apply to any data. /// /// Set this value to null to have no transform applied. public ICryptoTransform CryptoTransform { set { cryptoTransform = value; if ( cryptoTransform != null ) { if ( rawData == clearText ) { if ( internalClearText == null ) { internalClearText = new byte[rawData.Length]; } clearText = internalClearText; } clearTextLength = rawLength; if ( available > 0 ) { cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available); } } else { clearText = rawData; clearTextLength = rawLength; } } } #endif #region Instance Fields int rawLength; byte[] rawData; int clearTextLength; byte[] clearText; #if false//!NETCF_1_0 byte[] internalClearText; #endif int available; #if false//!NETCF_1_0 ICryptoTransform cryptoTransform; #endif Stream inputStream; #endregion } /// /// This filter stream is used to decompress data compressed using the "deflate" /// format. The "deflate" format is described in RFC 1951. /// /// This stream may form the basis for other decompression filters, such /// as the GZipInputStream. /// /// Author of the original java version: John Leuner. /// internal class InflaterInputStream : Stream { #region Constructors /// /// Create an InflaterInputStream with the default decompressor /// and a default buffer size of 4KB. /// /// /// The InputStream to read bytes from /// public InflaterInputStream(Stream baseInputStream) : this(baseInputStream, new Inflater(), 4096) { } /// /// Create an InflaterInputStream with the specified decompressor /// and a default buffer size of 4KB. /// /// /// The source of input data /// /// /// The decompressor used to decompress data read from baseInputStream /// public InflaterInputStream(Stream baseInputStream, Inflater inf) : this(baseInputStream, inf, 4096) { } /// /// Create an InflaterInputStream with the specified decompressor /// and the specified buffer size. /// /// /// The InputStream to read bytes from /// /// /// The decompressor to use /// /// /// Size of the buffer to use /// public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) { if (baseInputStream == null) { throw new ArgumentNullException("baseInputStream"); } if (inflater == null) { throw new ArgumentNullException("inflater"); } if (bufferSize <= 0) { throw new ArgumentOutOfRangeException("bufferSize"); } this.baseInputStream = baseInputStream; this.inf = inflater; inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); } #endregion /// /// Get/set flag indicating ownership of underlying stream. /// When the flag is true will close the underlying stream also. /// /// /// The default value is true. /// public bool IsStreamOwner { get { return isStreamOwner; } set { isStreamOwner = value; } } /// /// Skip specified number of bytes of uncompressed data /// /// /// Number of bytes to skip /// /// /// The number of bytes skipped, zero if the end of /// stream has been reached /// /// /// The number of bytes to skip is less than or equal to zero. /// public long Skip(long count) { if (count <= 0) { throw new ArgumentOutOfRangeException("count"); } // v0.80 Skip by seeking if underlying stream supports it... if (baseInputStream.CanSeek) { baseInputStream.Seek(count, SeekOrigin.Current); return count; } else { int length = 2048; if (count < length) { length = (int)count; } byte[] tmp = new byte[length]; int readCount = 1; long toSkip = count; while ((toSkip > 0) && (readCount > 0)) { if (toSkip < length) { length = (int)toSkip; } readCount = baseInputStream.Read(tmp, 0, length); toSkip -= readCount; } return count - toSkip; } } /// /// Clear any cryptographic state. /// protected void StopDecrypting() { #if false//!NETCF_1_0 inputBuffer.CryptoTransform = null; #endif } /// /// Returns 0 once the end of the stream (EOF) has been reached. /// Otherwise returns 1. /// public virtual int Available { get { return inf.IsFinished ? 0 : 1; } } /// /// Fills the buffer with more data to decompress. /// /// /// Stream ends early /// protected void Fill() { // Protect against redundant calls if (inputBuffer.Available <= 0) { inputBuffer.Fill(); if (inputBuffer.Available <= 0) { throw new SharpZipBaseException("Unexpected EOF"); } } inputBuffer.SetInflaterInput(inf); } #region Stream Overrides /// /// Gets a value indicating whether the current stream supports reading /// public override bool CanRead { get { return baseInputStream.CanRead; } } /// /// Gets a value of false indicating seeking is not supported for this stream. /// public override bool CanSeek { get { return false; } } /// /// Gets a value of false indicating that this stream is not writeable. /// public override bool CanWrite { get { return false; } } /// /// A value representing the length of the stream in bytes. /// public override long Length { get { return inputBuffer.RawLength; } } /// /// The current position within the stream. /// Throws a NotSupportedException when attempting to set the position /// /// Attempting to set the position public override long Position { get { return baseInputStream.Position; } set { throw new NotSupportedException("InflaterInputStream Position not supported"); } } /// /// Flushes the baseInputStream /// public override void Flush() { baseInputStream.Flush(); } /// /// Sets the position within the current stream /// Always throws a NotSupportedException /// /// The relative offset to seek to. /// The defining where to seek from. /// The new position in the stream. /// Any access public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException("Seek not supported"); } /// /// Set the length of the current stream /// Always throws a NotSupportedException /// /// The new length value for the stream. /// Any access public override void SetLength(long value) { throw new NotSupportedException("InflaterInputStream SetLength not supported"); } /// /// Writes a sequence of bytes to stream and advances the current position /// This method always throws a NotSupportedException /// /// Thew buffer containing data to write. /// The offset of the first byte to write. /// The number of bytes to write. /// Any access public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException("InflaterInputStream Write not supported"); } /// /// Writes one byte to the current stream and advances the current position /// Always throws a NotSupportedException /// /// The byte to write. /// Any access public override void WriteByte(byte value) { throw new NotSupportedException("InflaterInputStream WriteByte not supported"); } #if !NETFX_CORE && !UWP /// /// Entry point to begin an asynchronous write. Always throws a NotSupportedException. /// /// The buffer to write data from /// Offset of first byte to write /// The maximum number of bytes to write /// The method to be called when the asynchronous write operation is completed /// A user-provided object that distinguishes this particular asynchronous write request from other requests /// An IAsyncResult that references the asynchronous write /// Any access public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { throw new NotSupportedException("InflaterInputStream BeginWrite not supported"); } #endif #if !NETFX_CORE && !UWP /// /// Closes the input stream. When /// is true the underlying stream is also closed. /// public override void Close() { if (!isClosed) { isClosed = true; if (isStreamOwner) { baseInputStream.Close(); } } } #else public void Close() { if (!isClosed) { isClosed = true; if (isStreamOwner) { //baseInputStream.Close(); baseInputStream.Dispose(); } } } #endif /// /// Reads decompressed data into the provided buffer byte array /// /// /// The array to read and decompress data into /// /// /// The offset indicating where the data should be placed /// /// /// The number of bytes to decompress /// /// The number of bytes read. Zero signals the end of stream /// /// Inflater needs a dictionary /// public override int Read(byte[] buffer, int offset, int count) { if (inf.IsNeedingDictionary) { throw new SharpZipBaseException("Need a dictionary"); } int remainingBytes = count; while (true) { int bytesRead = inf.Inflate(buffer, offset, remainingBytes); offset += bytesRead; remainingBytes -= bytesRead; if (remainingBytes == 0 || inf.IsFinished) { break; } if (inf.IsNeedingInput) { try { Fill(); } catch (SharpZipBaseException ex) { // Hack 16-05-25: Some PDF files lead to an "Unexpected EOF" exception. Is it safe to ignore this exception? if (ex.Message != "Unexpected EOF") throw; // WB! early EOF: apparently not a big deal for some PDF pages: break out of the loop. break; } } else if (bytesRead == 0) { throw new ZipException("Don't know what to do"); } } return count - remainingBytes; } #endregion #region Instance Fields /// /// Decompressor for this stream /// protected Inflater inf; /// /// Input buffer for this stream. /// protected InflaterInputBuffer inputBuffer; /// /// Base stream the inflater reads from. /// private Stream baseInputStream; ///// ///// The compressed size ///// ////protected long csize; #if true || !NETFX_CORE /// /// Flag indicating wether this instance has been closed or not. /// bool isClosed; #endif /// /// Flag indicating wether this instance is designated the stream owner. /// When closing if this flag is true the underlying stream is closed. /// bool isStreamOwner = true; #endregion } }