初始化上传

This commit is contained in:
2025-08-26 08:37:44 +08:00
commit 31d81b91b6
448 changed files with 80981 additions and 0 deletions

View File

@@ -0,0 +1,438 @@
using ICSharpCode.SharpZipLib.Encryption;
using System;
using System.IO;
using System.Security.Cryptography;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
/// <summary>
/// A special stream deflating or compressing the bytes that are
/// written to it. It uses a Deflater to perform actual deflating.<br/>
/// Authors of the original java version : Tom Tromey, Jochen Hoenicke
/// </summary>
public class DeflaterOutputStream : Stream
{
#region Constructors
/// <summary>
/// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
/// </summary>
/// <param name="baseOutputStream">
/// the output stream where deflated output should be written.
/// </param>
public DeflaterOutputStream(Stream baseOutputStream)
: this(baseOutputStream, new Deflater(), 512)
{
}
/// <summary>
/// Creates a new DeflaterOutputStream with the given Deflater and
/// default buffer size.
/// </summary>
/// <param name="baseOutputStream">
/// the output stream where deflated output should be written.
/// </param>
/// <param name="deflater">
/// the underlying deflater.
/// </param>
public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
: this(baseOutputStream, deflater, 512)
{
}
/// <summary>
/// Creates a new DeflaterOutputStream with the given Deflater and
/// buffer size.
/// </summary>
/// <param name="baseOutputStream">
/// The output stream where deflated output is written.
/// </param>
/// <param name="deflater">
/// The underlying deflater to use
/// </param>
/// <param name="bufferSize">
/// The buffer size in bytes to use when deflating (minimum value 512)
/// </param>
/// <exception cref="ArgumentOutOfRangeException">
/// bufsize is less than or equal to zero.
/// </exception>
/// <exception cref="ArgumentException">
/// baseOutputStream does not support writing
/// </exception>
/// <exception cref="ArgumentNullException">
/// deflater instance is null
/// </exception>
public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
{
if (baseOutputStream == null)
{
throw new ArgumentNullException(nameof(baseOutputStream));
}
if (baseOutputStream.CanWrite == false)
{
throw new ArgumentException("Must support writing", nameof(baseOutputStream));
}
if (bufferSize < 512)
{
throw new ArgumentOutOfRangeException(nameof(bufferSize));
}
baseOutputStream_ = baseOutputStream;
buffer_ = new byte[bufferSize];
deflater_ = deflater ?? throw new ArgumentNullException(nameof(deflater));
}
#endregion Constructors
#region Public API
/// <summary>
/// Finishes the stream by calling finish() on the deflater.
/// </summary>
/// <exception cref="SharpZipBaseException">
/// Not all input is deflated
/// </exception>
public virtual void Finish()
{
deflater_.Finish();
while (!deflater_.IsFinished)
{
int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
if (len <= 0)
{
break;
}
if (cryptoTransform_ != null)
{
EncryptBlock(buffer_, 0, len);
}
baseOutputStream_.Write(buffer_, 0, len);
}
if (!deflater_.IsFinished)
{
throw new SharpZipBaseException("Can't deflate all input?");
}
baseOutputStream_.Flush();
if (cryptoTransform_ != null)
{
if (cryptoTransform_ is ZipAESTransform)
{
AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
}
cryptoTransform_.Dispose();
cryptoTransform_ = null;
}
}
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner { get; set; } = true;
/// <summary>
/// Allows client to determine if an entry can be patched after its added
/// </summary>
public bool CanPatchEntries
{
get
{
return baseOutputStream_.CanSeek;
}
}
#endregion Public API
#region Encryption
/// <summary>
/// The CryptoTransform currently being used to encrypt the compressed data.
/// </summary>
protected ICryptoTransform cryptoTransform_;
/// <summary>
/// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
/// </summary>
protected byte[] AESAuthCode;
/// <summary>
/// Encrypt a block of data
/// </summary>
/// <param name="buffer">
/// Data to encrypt. NOTE the original contents of the buffer are lost
/// </param>
/// <param name="offset">
/// Offset of first byte in buffer to encrypt
/// </param>
/// <param name="length">
/// Number of bytes in buffer to encrypt
/// </param>
protected void EncryptBlock(byte[] buffer, int offset, int length)
{
cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
}
#endregion Encryption
#region Deflation Support
/// <summary>
/// Deflates everything in the input buffers. This will call
/// <code>def.deflate()</code> until all bytes from the input buffers
/// are processed.
/// </summary>
protected void Deflate()
{
Deflate(false);
}
private void Deflate(bool flushing)
{
while (flushing || !deflater_.IsNeedingInput)
{
int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
if (deflateCount <= 0)
{
break;
}
if (cryptoTransform_ != null)
{
EncryptBlock(buffer_, 0, deflateCount);
}
baseOutputStream_.Write(buffer_, 0, deflateCount);
}
if (!deflater_.IsNeedingInput)
{
throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
}
}
#endregion Deflation Support
#region Stream Overrides
/// <summary>
/// Gets value indicating stream can be read from
/// </summary>
public override bool CanRead
{
get
{
return false;
}
}
/// <summary>
/// Gets a value indicating if seeking is supported for this stream
/// This property always returns false
/// </summary>
public override bool CanSeek
{
get
{
return false;
}
}
/// <summary>
/// Get value indicating if this stream supports writing
/// </summary>
public override bool CanWrite
{
get
{
return baseOutputStream_.CanWrite;
}
}
/// <summary>
/// Get current length of stream
/// </summary>
public override long Length
{
get
{
return baseOutputStream_.Length;
}
}
/// <summary>
/// Gets the current position within the stream.
/// </summary>
/// <exception cref="NotSupportedException">Any attempt to set position</exception>
public override long Position
{
get
{
return baseOutputStream_.Position;
}
set
{
throw new NotSupportedException("Position property not supported");
}
}
/// <summary>
/// Sets the current position of this stream to the given value. Not supported by this class!
/// </summary>
/// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
/// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
/// <returns>The new position in the stream.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("DeflaterOutputStream Seek not supported");
}
/// <summary>
/// Sets the length of this stream to the given value. Not supported by this class!
/// </summary>
/// <param name="value">The new stream length.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void SetLength(long value)
{
throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
}
/// <summary>
/// Read a byte from stream advancing position by one
/// </summary>
/// <returns>The byte read cast to an int. THe value is -1 if at the end of the stream.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override int ReadByte()
{
throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
}
/// <summary>
/// Read a block of bytes from stream
/// </summary>
/// <param name="buffer">The buffer to store read data in.</param>
/// <param name="offset">The offset to start storing at.</param>
/// <param name="count">The maximum number of bytes to read.</param>
/// <returns>The actual number of bytes read. Zero if end of stream is detected.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("DeflaterOutputStream Read not supported");
}
/// <summary>
/// Flushes the stream by calling <see cref="Flush">Flush</see> on the deflater and then
/// on the underlying stream. This ensures that all bytes are flushed.
/// </summary>
public override void Flush()
{
deflater_.Flush();
Deflate(true);
baseOutputStream_.Flush();
}
/// <summary>
/// Calls <see cref="Finish"/> and closes the underlying
/// stream when <see cref="IsStreamOwner"></see> is true.
/// </summary>
protected override void Dispose(bool disposing)
{
if (!isClosed_)
{
isClosed_ = true;
try
{
Finish();
if (cryptoTransform_ != null)
{
GetAuthCodeIfAES();
cryptoTransform_.Dispose();
cryptoTransform_ = null;
}
}
finally
{
if (IsStreamOwner)
{
baseOutputStream_.Dispose();
}
}
}
}
/// <summary>
/// Get the Auth code for AES encrypted entries
/// </summary>
protected void GetAuthCodeIfAES()
{
if (cryptoTransform_ is ZipAESTransform)
{
AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
}
}
/// <summary>
/// Writes a single byte to the compressed output stream.
/// </summary>
/// <param name="value">
/// The byte value.
/// </param>
public override void WriteByte(byte value)
{
byte[] b = new byte[1];
b[0] = value;
Write(b, 0, 1);
}
/// <summary>
/// Writes bytes from an array to the compressed stream.
/// </summary>
/// <param name="buffer">
/// The byte array
/// </param>
/// <param name="offset">
/// The offset into the byte array where to start.
/// </param>
/// <param name="count">
/// The number of bytes to write.
/// </param>
public override void Write(byte[] buffer, int offset, int count)
{
deflater_.SetInput(buffer, offset, count);
Deflate();
}
#endregion Stream Overrides
#region Instance Fields
/// <summary>
/// This buffer is used temporarily to retrieve the bytes from the
/// deflater and write them to the underlying output stream.
/// </summary>
private byte[] buffer_;
/// <summary>
/// The deflater which is used to deflate the stream.
/// </summary>
protected Deflater deflater_;
/// <summary>
/// Base stream the deflater depends on.
/// </summary>
protected Stream baseOutputStream_;
private bool isClosed_;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,713 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
/// <summary>
/// An input buffer customised for use by <see cref="InflaterInputStream"/>
/// </summary>
/// <remarks>
/// The buffer supports decryption of incoming data.
/// </remarks>
public class InflaterInputBuffer
{
#region Constructors
/// <summary>
/// Initialise a new instance of <see cref="InflaterInputBuffer"/> with a default buffer size
/// </summary>
/// <param name="stream">The stream to buffer.</param>
public InflaterInputBuffer(Stream stream) : this(stream, 4096)
{
}
/// <summary>
/// Initialise a new instance of <see cref="InflaterInputBuffer"/>
/// </summary>
/// <param name="stream">The stream to buffer.</param>
/// <param name="bufferSize">The size to use for the buffer</param>
/// <remarks>A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB.</remarks>
public InflaterInputBuffer(Stream stream, int bufferSize)
{
inputStream = stream;
if (bufferSize < 1024)
{
bufferSize = 1024;
}
rawData = new byte[bufferSize];
clearText = rawData;
}
#endregion Constructors
/// <summary>
/// Get the length of bytes in the <see cref="RawData"/>
/// </summary>
public int RawLength
{
get
{
return rawLength;
}
}
/// <summary>
/// Get the contents of the raw data buffer.
/// </summary>
/// <remarks>This may contain encrypted data.</remarks>
public byte[] RawData
{
get
{
return rawData;
}
}
/// <summary>
/// Get the number of useable bytes in <see cref="ClearText"/>
/// </summary>
public int ClearTextLength
{
get
{
return clearTextLength;
}
}
/// <summary>
/// Get the contents of the clear text buffer.
/// </summary>
public byte[] ClearText
{
get
{
return clearText;
}
}
/// <summary>
/// Get/set the number of bytes available
/// </summary>
public int Available
{
get { return available; }
set { available = value; }
}
/// <summary>
/// Call <see cref="Inflater.SetInput(byte[], int, int)"/> passing the current clear text buffer contents.
/// </summary>
/// <param name="inflater">The inflater to set input for.</param>
public void SetInflaterInput(Inflater inflater)
{
if (available > 0)
{
inflater.SetInput(clearText, clearTextLength - available, available);
available = 0;
}
}
/// <summary>
/// Fill the buffer from the underlying input stream.
/// </summary>
public void Fill()
{
rawLength = 0;
int toRead = rawData.Length;
while (toRead > 0 && inputStream.CanRead)
{
int count = inputStream.Read(rawData, rawLength, toRead);
if (count <= 0)
{
break;
}
rawLength += count;
toRead -= count;
}
if (cryptoTransform != null)
{
clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
}
else
{
clearTextLength = rawLength;
}
available = clearTextLength;
}
/// <summary>
/// Read a buffer directly from the input stream
/// </summary>
/// <param name="buffer">The buffer to fill</param>
/// <returns>Returns the number of bytes read.</returns>
public int ReadRawBuffer(byte[] buffer)
{
return ReadRawBuffer(buffer, 0, buffer.Length);
}
/// <summary>
/// Read a buffer directly from the input stream
/// </summary>
/// <param name="outBuffer">The buffer to read into</param>
/// <param name="offset">The offset to start reading data into.</param>
/// <param name="length">The number of bytes to read.</param>
/// <returns>Returns the number of bytes read.</returns>
public int ReadRawBuffer(byte[] outBuffer, int offset, int length)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(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;
}
/// <summary>
/// Read clear text data from the input stream.
/// </summary>
/// <param name="outBuffer">The buffer to add data to.</param>
/// <param name="offset">The offset to start adding data at.</param>
/// <param name="length">The number of bytes to read.</param>
/// <returns>Returns the number of bytes actually read.</returns>
public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(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;
}
/// <summary>
/// Read a <see cref="byte"/> from the input stream.
/// </summary>
/// <returns>Returns the byte read.</returns>
public byte ReadLeByte()
{
if (available <= 0)
{
Fill();
if (available <= 0)
{
throw new ZipException("EOF in header");
}
}
byte result = rawData[rawLength - available];
available -= 1;
return result;
}
/// <summary>
/// Read an <see cref="short"/> in little endian byte order.
/// </summary>
/// <returns>The short value read case to an int.</returns>
public int ReadLeShort()
{
return ReadLeByte() | (ReadLeByte() << 8);
}
/// <summary>
/// Read an <see cref="int"/> in little endian byte order.
/// </summary>
/// <returns>The int value read.</returns>
public int ReadLeInt()
{
return ReadLeShort() | (ReadLeShort() << 16);
}
/// <summary>
/// Read a <see cref="long"/> in little endian byte order.
/// </summary>
/// <returns>The long value read.</returns>
public long ReadLeLong()
{
return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
}
/// <summary>
/// Get/set the <see cref="ICryptoTransform"/> to apply to any data.
/// </summary>
/// <remarks>Set this value to null to have no transform applied.</remarks>
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;
}
}
}
#region Instance Fields
private int rawLength;
private byte[] rawData;
private int clearTextLength;
private byte[] clearText;
private byte[] internalClearText;
private int available;
private ICryptoTransform cryptoTransform;
private Stream inputStream;
#endregion Instance Fields
}
/// <summary>
/// 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 <see cref="ICSharpCode.SharpZipLib.GZip.GZipInputStream">GZipInputStream</see>.
///
/// Author of the original java version : John Leuner.
/// </summary>
public class InflaterInputStream : Stream
{
#region Constructors
/// <summary>
/// Create an InflaterInputStream with the default decompressor
/// and a default buffer size of 4KB.
/// </summary>
/// <param name = "baseInputStream">
/// The InputStream to read bytes from
/// </param>
public InflaterInputStream(Stream baseInputStream)
: this(baseInputStream, new Inflater(), 4096)
{
}
/// <summary>
/// Create an InflaterInputStream with the specified decompressor
/// and a default buffer size of 4KB.
/// </summary>
/// <param name = "baseInputStream">
/// The source of input data
/// </param>
/// <param name = "inf">
/// The decompressor used to decompress data read from baseInputStream
/// </param>
public InflaterInputStream(Stream baseInputStream, Inflater inf)
: this(baseInputStream, inf, 4096)
{
}
/// <summary>
/// Create an InflaterInputStream with the specified decompressor
/// and the specified buffer size.
/// </summary>
/// <param name = "baseInputStream">
/// The InputStream to read bytes from
/// </param>
/// <param name = "inflater">
/// The decompressor to use
/// </param>
/// <param name = "bufferSize">
/// Size of the buffer to use
/// </param>
public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize)
{
if (baseInputStream == null)
{
throw new ArgumentNullException(nameof(baseInputStream));
}
if (inflater == null)
{
throw new ArgumentNullException(nameof(inflater));
}
if (bufferSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(bufferSize));
}
this.baseInputStream = baseInputStream;
this.inf = inflater;
inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize);
}
#endregion Constructors
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
/// </summary>
/// <remarks>The default value is true.</remarks>
public bool IsStreamOwner { get; set; } = true;
/// <summary>
/// Skip specified number of bytes of uncompressed data
/// </summary>
/// <param name ="count">
/// Number of bytes to skip
/// </param>
/// <returns>
/// The number of bytes skipped, zero if the end of
/// stream has been reached
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="count">The number of bytes</paramref> to skip is less than or equal to zero.
/// </exception>
public long Skip(long count)
{
if (count <= 0)
{
throw new ArgumentOutOfRangeException(nameof(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;
}
}
/// <summary>
/// Clear any cryptographic state.
/// </summary>
protected void StopDecrypting()
{
inputBuffer.CryptoTransform = null;
}
/// <summary>
/// Returns 0 once the end of the stream (EOF) has been reached.
/// Otherwise returns 1.
/// </summary>
public virtual int Available
{
get
{
return inf.IsFinished ? 0 : 1;
}
}
/// <summary>
/// Fills the buffer with more data to decompress.
/// </summary>
/// <exception cref="SharpZipBaseException">
/// Stream ends early
/// </exception>
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
/// <summary>
/// Gets a value indicating whether the current stream supports reading
/// </summary>
public override bool CanRead
{
get
{
return baseInputStream.CanRead;
}
}
/// <summary>
/// Gets a value of false indicating seeking is not supported for this stream.
/// </summary>
public override bool CanSeek
{
get
{
return false;
}
}
/// <summary>
/// Gets a value of false indicating that this stream is not writeable.
/// </summary>
public override bool CanWrite
{
get
{
return false;
}
}
/// <summary>
/// A value representing the length of the stream in bytes.
/// </summary>
public override long Length
{
get
{
//return inputBuffer.RawLength;
throw new NotSupportedException("InflaterInputStream Length is not supported");
}
}
/// <summary>
/// The current position within the stream.
/// Throws a NotSupportedException when attempting to set the position
/// </summary>
/// <exception cref="NotSupportedException">Attempting to set the position</exception>
public override long Position
{
get
{
return baseInputStream.Position;
}
set
{
throw new NotSupportedException("InflaterInputStream Position not supported");
}
}
/// <summary>
/// Flushes the baseInputStream
/// </summary>
public override void Flush()
{
baseInputStream.Flush();
}
/// <summary>
/// Sets the position within the current stream
/// Always throws a NotSupportedException
/// </summary>
/// <param name="offset">The relative offset to seek to.</param>
/// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
/// <returns>The new position in the stream.</returns>
/// <exception cref="NotSupportedException">Any access</exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException("Seek not supported");
}
/// <summary>
/// Set the length of the current stream
/// Always throws a NotSupportedException
/// </summary>
/// <param name="value">The new length value for the stream.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void SetLength(long value)
{
throw new NotSupportedException("InflaterInputStream SetLength not supported");
}
/// <summary>
/// Writes a sequence of bytes to stream and advances the current position
/// This method always throws a NotSupportedException
/// </summary>
/// <param name="buffer">The buffer containing data to write.</param>
/// <param name="offset">The offset of the first byte to write.</param>
/// <param name="count">The number of bytes to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException("InflaterInputStream Write not supported");
}
/// <summary>
/// Writes one byte to the current stream and advances the current position
/// Always throws a NotSupportedException
/// </summary>
/// <param name="value">The byte to write.</param>
/// <exception cref="NotSupportedException">Any access</exception>
public override void WriteByte(byte value)
{
throw new NotSupportedException("InflaterInputStream WriteByte not supported");
}
/// <summary>
/// Closes the input stream. When <see cref="IsStreamOwner"></see>
/// is true the underlying stream is also closed.
/// </summary>
protected override void Dispose(bool disposing)
{
if (!isClosed)
{
isClosed = true;
if (IsStreamOwner)
{
baseInputStream.Dispose();
}
}
}
/// <summary>
/// Reads decompressed data into the provided buffer byte array
/// </summary>
/// <param name ="buffer">
/// The array to read and decompress data into
/// </param>
/// <param name ="offset">
/// The offset indicating where the data should be placed
/// </param>
/// <param name ="count">
/// The number of bytes to decompress
/// </param>
/// <returns>The number of bytes read. Zero signals the end of stream</returns>
/// <exception cref="SharpZipBaseException">
/// Inflater needs a dictionary
/// </exception>
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)
{
Fill();
}
else if (bytesRead == 0)
{
throw new ZipException("Invalid input data");
}
}
return count - remainingBytes;
}
#endregion Stream Overrides
#region Instance Fields
/// <summary>
/// Decompressor for this stream
/// </summary>
protected Inflater inf;
/// <summary>
/// <see cref="InflaterInputBuffer">Input buffer</see> for this stream.
/// </summary>
protected InflaterInputBuffer inputBuffer;
/// <summary>
/// Base stream the inflater reads from.
/// </summary>
private Stream baseInputStream;
/// <summary>
/// The compressed size
/// </summary>
protected long csize;
/// <summary>
/// Flag indicating whether this instance has been closed or not.
/// </summary>
private bool isClosed;
#endregion Instance Fields
}
}

View File

@@ -0,0 +1,220 @@
using System;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
/// <summary>
/// Contains the output from the Inflation process.
/// We need to have a window so that we can refer backwards into the output stream
/// to repeat stuff.<br/>
/// Author of the original java version : John Leuner
/// </summary>
public class OutputWindow
{
#region Constants
private const int WindowSize = 1 << 15;
private const int WindowMask = WindowSize - 1;
#endregion Constants
#region Instance Fields
private byte[] window = new byte[WindowSize]; //The window is 2^15 bytes
private int windowEnd;
private int windowFilled;
#endregion Instance Fields
/// <summary>
/// Write a byte to this output window
/// </summary>
/// <param name="value">value to write</param>
/// <exception cref="InvalidOperationException">
/// if window is full
/// </exception>
public void Write(int value)
{
if (windowFilled++ == WindowSize)
{
throw new InvalidOperationException("Window full");
}
window[windowEnd++] = (byte)value;
windowEnd &= WindowMask;
}
private void SlowRepeat(int repStart, int length, int distance)
{
while (length-- > 0)
{
window[windowEnd++] = window[repStart++];
windowEnd &= WindowMask;
repStart &= WindowMask;
}
}
/// <summary>
/// Append a byte pattern already in the window itself
/// </summary>
/// <param name="length">length of pattern to copy</param>
/// <param name="distance">distance from end of window pattern occurs</param>
/// <exception cref="InvalidOperationException">
/// If the repeated data overflows the window
/// </exception>
public void Repeat(int length, int distance)
{
if ((windowFilled += length) > WindowSize)
{
throw new InvalidOperationException("Window full");
}
int repStart = (windowEnd - distance) & WindowMask;
int border = WindowSize - length;
if ((repStart <= border) && (windowEnd < border))
{
if (length <= distance)
{
System.Array.Copy(window, repStart, window, windowEnd, length);
windowEnd += length;
}
else
{
// We have to copy manually, since the repeat pattern overlaps.
while (length-- > 0)
{
window[windowEnd++] = window[repStart++];
}
}
}
else
{
SlowRepeat(repStart, length, distance);
}
}
/// <summary>
/// Copy from input manipulator to internal window
/// </summary>
/// <param name="input">source of data</param>
/// <param name="length">length of data to copy</param>
/// <returns>the number of bytes copied</returns>
public int CopyStored(StreamManipulator input, int length)
{
length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes);
int copied;
int tailLen = WindowSize - windowEnd;
if (length > tailLen)
{
copied = input.CopyBytes(window, windowEnd, tailLen);
if (copied == tailLen)
{
copied += input.CopyBytes(window, 0, length - tailLen);
}
}
else
{
copied = input.CopyBytes(window, windowEnd, length);
}
windowEnd = (windowEnd + copied) & WindowMask;
windowFilled += copied;
return copied;
}
/// <summary>
/// Copy dictionary to window
/// </summary>
/// <param name="dictionary">source dictionary</param>
/// <param name="offset">offset of start in source dictionary</param>
/// <param name="length">length of dictionary</param>
/// <exception cref="InvalidOperationException">
/// If window isnt empty
/// </exception>
public void CopyDict(byte[] dictionary, int offset, int length)
{
if (dictionary == null)
{
throw new ArgumentNullException(nameof(dictionary));
}
if (windowFilled > 0)
{
throw new InvalidOperationException();
}
if (length > WindowSize)
{
offset += length - WindowSize;
length = WindowSize;
}
System.Array.Copy(dictionary, offset, window, 0, length);
windowEnd = length & WindowMask;
}
/// <summary>
/// Get remaining unfilled space in window
/// </summary>
/// <returns>Number of bytes left in window</returns>
public int GetFreeSpace()
{
return WindowSize - windowFilled;
}
/// <summary>
/// Get bytes available for output in window
/// </summary>
/// <returns>Number of bytes filled</returns>
public int GetAvailable()
{
return windowFilled;
}
/// <summary>
/// Copy contents of window to output
/// </summary>
/// <param name="output">buffer to copy to</param>
/// <param name="offset">offset to start at</param>
/// <param name="len">number of bytes to count</param>
/// <returns>The number of bytes copied</returns>
/// <exception cref="InvalidOperationException">
/// If a window underflow occurs
/// </exception>
public int CopyOutput(byte[] output, int offset, int len)
{
int copyEnd = windowEnd;
if (len > windowFilled)
{
len = windowFilled;
}
else
{
copyEnd = (windowEnd - windowFilled + len) & WindowMask;
}
int copied = len;
int tailLen = len - copyEnd;
if (tailLen > 0)
{
System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen);
offset += tailLen;
len = copyEnd;
}
System.Array.Copy(window, copyEnd - len, output, offset, len);
windowFilled -= copied;
if (windowFilled < 0)
{
throw new InvalidOperationException();
}
return copied;
}
/// <summary>
/// Reset by clearing window so <see cref="GetAvailable">GetAvailable</see> returns 0
/// </summary>
public void Reset()
{
windowFilled = windowEnd = 0;
}
}
}

View File

@@ -0,0 +1,298 @@
using System;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
/// <summary>
/// This class allows us to retrieve a specified number of bits from
/// the input buffer, as well as copy big byte blocks.
///
/// It uses an int buffer to store up to 31 bits for direct
/// manipulation. This guarantees that we can get at least 16 bits,
/// but we only need at most 15, so this is all safe.
///
/// There are some optimizations in this class, for example, you must
/// never peek more than 8 bits more than needed, and you must first
/// peek bits before you may drop them. This is not a general purpose
/// class but optimized for the behaviour of the Inflater.
///
/// authors of the original java version : John Leuner, Jochen Hoenicke
/// </summary>
public class StreamManipulator
{
/// <summary>
/// Get the next sequence of bits but don't increase input pointer. bitCount must be
/// less or equal 16 and if this call succeeds, you must drop
/// at least n - 8 bits in the next call.
/// </summary>
/// <param name="bitCount">The number of bits to peek.</param>
/// <returns>
/// the value of the bits, or -1 if not enough bits available. */
/// </returns>
public int PeekBits(int bitCount)
{
if (bitsInBuffer_ < bitCount)
{
if (windowStart_ == windowEnd_)
{
return -1; // ok
}
buffer_ |= (uint)((window_[windowStart_++] & 0xff |
(window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_);
bitsInBuffer_ += 16;
}
return (int)(buffer_ & ((1 << bitCount) - 1));
}
/// <summary>
/// Tries to grab the next <paramref name="bitCount"/> bits from the input and
/// sets <paramref name="output"/> to the value, adding <paramref name="outputOffset"/>.
/// </summary>
/// <returns>true if enough bits could be read, otherwise false</returns>
public bool TryGetBits(int bitCount, ref int output, int outputOffset = 0)
{
var bits = PeekBits(bitCount);
if (bits < 0)
{
return false;
}
output = bits + outputOffset;
DropBits(bitCount);
return true;
}
/// <summary>
/// Tries to grab the next <paramref name="bitCount"/> bits from the input and
/// sets <paramref name="index"/> of <paramref name="array"/> to the value.
/// </summary>
/// <returns>true if enough bits could be read, otherwise false</returns>
public bool TryGetBits(int bitCount, ref byte[] array, int index)
{
var bits = PeekBits(bitCount);
if (bits < 0)
{
return false;
}
array[index] = (byte)bits;
DropBits(bitCount);
return true;
}
/// <summary>
/// Drops the next n bits from the input. You should have called PeekBits
/// with a bigger or equal n before, to make sure that enough bits are in
/// the bit buffer.
/// </summary>
/// <param name="bitCount">The number of bits to drop.</param>
public void DropBits(int bitCount)
{
buffer_ >>= bitCount;
bitsInBuffer_ -= bitCount;
}
/// <summary>
/// Gets the next n bits and increases input pointer. This is equivalent
/// to <see cref="PeekBits"/> followed by <see cref="DropBits"/>, except for correct error handling.
/// </summary>
/// <param name="bitCount">The number of bits to retrieve.</param>
/// <returns>
/// the value of the bits, or -1 if not enough bits available.
/// </returns>
public int GetBits(int bitCount)
{
int bits = PeekBits(bitCount);
if (bits >= 0)
{
DropBits(bitCount);
}
return bits;
}
/// <summary>
/// Gets the number of bits available in the bit buffer. This must be
/// only called when a previous PeekBits() returned -1.
/// </summary>
/// <returns>
/// the number of bits available.
/// </returns>
public int AvailableBits
{
get
{
return bitsInBuffer_;
}
}
/// <summary>
/// Gets the number of bytes available.
/// </summary>
/// <returns>
/// The number of bytes available.
/// </returns>
public int AvailableBytes
{
get
{
return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3);
}
}
/// <summary>
/// Skips to the next byte boundary.
/// </summary>
public void SkipToByteBoundary()
{
buffer_ >>= (bitsInBuffer_ & 7);
bitsInBuffer_ &= ~7;
}
/// <summary>
/// Returns true when SetInput can be called
/// </summary>
public bool IsNeedingInput
{
get
{
return windowStart_ == windowEnd_;
}
}
/// <summary>
/// Copies bytes from input buffer to output buffer starting
/// at output[offset]. You have to make sure, that the buffer is
/// byte aligned. If not enough bytes are available, copies fewer
/// bytes.
/// </summary>
/// <param name="output">
/// The buffer to copy bytes to.
/// </param>
/// <param name="offset">
/// The offset in the buffer at which copying starts
/// </param>
/// <param name="length">
/// The length to copy, 0 is allowed.
/// </param>
/// <returns>
/// The number of bytes copied, 0 if no bytes were available.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Length is less than zero
/// </exception>
/// <exception cref="InvalidOperationException">
/// Bit buffer isnt byte aligned
/// </exception>
public int CopyBytes(byte[] output, int offset, int length)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
if ((bitsInBuffer_ & 7) != 0)
{
// bits_in_buffer may only be 0 or a multiple of 8
throw new InvalidOperationException("Bit buffer is not byte aligned!");
}
int count = 0;
while ((bitsInBuffer_ > 0) && (length > 0))
{
output[offset++] = (byte)buffer_;
buffer_ >>= 8;
bitsInBuffer_ -= 8;
length--;
count++;
}
if (length == 0)
{
return count;
}
int avail = windowEnd_ - windowStart_;
if (length > avail)
{
length = avail;
}
System.Array.Copy(window_, windowStart_, output, offset, length);
windowStart_ += length;
if (((windowStart_ - windowEnd_) & 1) != 0)
{
// We always want an even number of bytes in input, see peekBits
buffer_ = (uint)(window_[windowStart_++] & 0xff);
bitsInBuffer_ = 8;
}
return count + length;
}
/// <summary>
/// Resets state and empties internal buffers
/// </summary>
public void Reset()
{
buffer_ = 0;
windowStart_ = windowEnd_ = bitsInBuffer_ = 0;
}
/// <summary>
/// Add more input for consumption.
/// Only call when IsNeedingInput returns true
/// </summary>
/// <param name="buffer">data to be input</param>
/// <param name="offset">offset of first byte of input</param>
/// <param name="count">number of bytes of input to add.</param>
public void SetInput(byte[] buffer, int offset, int count)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be negative");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), "Cannot be negative");
}
if (windowStart_ < windowEnd_)
{
throw new InvalidOperationException("Old input was not completely processed");
}
int end = offset + count;
// We want to throw an ArrayIndexOutOfBoundsException early.
// Note the check also handles integer wrap around.
if ((offset > end) || (end > buffer.Length))
{
throw new ArgumentOutOfRangeException(nameof(count));
}
if ((count & 1) != 0)
{
// We always want an even number of bytes in input, see PeekBits
buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_);
bitsInBuffer_ += 8;
}
window_ = buffer;
windowStart_ = offset;
windowEnd_ = end;
}
#region Instance Fields
private byte[] window_;
private int windowStart_;
private int windowEnd_;
private uint buffer_;
private int bitsInBuffer_;
#endregion Instance Fields
}
}