You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
11 KiB
301 lines
11 KiB
// |
|
// © Copyright Henrik Ravn 2004 |
|
// |
|
// Use, modification and distribution are subject to the Boost Software License, Version 1.0. |
|
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
// |
|
|
|
using System; |
|
using System.IO; |
|
using System.Runtime.InteropServices; |
|
|
|
namespace DotZLib |
|
{ |
|
/// <summary> |
|
/// Implements a compressed <see cref="Stream"/>, in GZip (.gz) format. |
|
/// </summary> |
|
public class GZipStream : Stream, IDisposable |
|
{ |
|
#region Dll Imports |
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] |
|
private static extern IntPtr gzopen(string name, string mode); |
|
|
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] |
|
private static extern int gzclose(IntPtr gzFile); |
|
|
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] |
|
private static extern int gzwrite(IntPtr gzFile, int data, int length); |
|
|
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] |
|
private static extern int gzread(IntPtr gzFile, int data, int length); |
|
|
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] |
|
private static extern int gzgetc(IntPtr gzFile); |
|
|
|
[DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] |
|
private static extern int gzputc(IntPtr gzFile, int c); |
|
|
|
#endregion |
|
|
|
#region Private data |
|
private IntPtr _gzFile; |
|
private bool _isDisposed = false; |
|
private bool _isWriting; |
|
#endregion |
|
|
|
#region Constructors |
|
/// <summary> |
|
/// Creates a new file as a writeable GZipStream |
|
/// </summary> |
|
/// <param name="fileName">The name of the compressed file to create</param> |
|
/// <param name="level">The compression level to use when adding data</param> |
|
/// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> |
|
public GZipStream(string fileName, CompressLevel level) |
|
{ |
|
_isWriting = true; |
|
_gzFile = gzopen(fileName, String.Format("wb{0}", (int)level)); |
|
if (_gzFile == IntPtr.Zero) |
|
throw new ZLibException(-1, "Could not open " + fileName); |
|
} |
|
|
|
/// <summary> |
|
/// Opens an existing file as a readable GZipStream |
|
/// </summary> |
|
/// <param name="fileName">The name of the file to open</param> |
|
/// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> |
|
public GZipStream(string fileName) |
|
{ |
|
_isWriting = false; |
|
_gzFile = gzopen(fileName, "rb"); |
|
if (_gzFile == IntPtr.Zero) |
|
throw new ZLibException(-1, "Could not open " + fileName); |
|
|
|
} |
|
#endregion |
|
|
|
#region Access properties |
|
/// <summary> |
|
/// Returns true of this stream can be read from, false otherwise |
|
/// </summary> |
|
public override bool CanRead |
|
{ |
|
get |
|
{ |
|
return !_isWriting; |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// Returns false. |
|
/// </summary> |
|
public override bool CanSeek |
|
{ |
|
get |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Returns true if this tsream is writeable, false otherwise |
|
/// </summary> |
|
public override bool CanWrite |
|
{ |
|
get |
|
{ |
|
return _isWriting; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Destructor & IDispose stuff |
|
|
|
/// <summary> |
|
/// Destroys this instance |
|
/// </summary> |
|
~GZipStream() |
|
{ |
|
cleanUp(false); |
|
} |
|
|
|
/// <summary> |
|
/// Closes the external file handle |
|
/// </summary> |
|
public void Dispose() |
|
{ |
|
cleanUp(true); |
|
} |
|
|
|
// Does the actual closing of the file handle. |
|
private void cleanUp(bool isDisposing) |
|
{ |
|
if (!_isDisposed) |
|
{ |
|
gzclose(_gzFile); |
|
_isDisposed = true; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Basic reading and writing |
|
/// <summary> |
|
/// Attempts to read a number of bytes from the stream. |
|
/// </summary> |
|
/// <param name="buffer">The destination data buffer</param> |
|
/// <param name="offset">The index of the first destination byte in <c>buffer</c></param> |
|
/// <param name="count">The number of bytes requested</param> |
|
/// <returns>The number of bytes read</returns> |
|
/// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> |
|
/// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> |
|
/// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> |
|
/// <exception cref="NotSupportedException">If this stream is not readable.</exception> |
|
/// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> |
|
public override int Read(byte[] buffer, int offset, int count) |
|
{ |
|
if (!CanRead) throw new NotSupportedException(); |
|
if (buffer == null) throw new ArgumentNullException(); |
|
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); |
|
if ((offset+count) > buffer.Length) throw new ArgumentException(); |
|
if (_isDisposed) throw new ObjectDisposedException("GZipStream"); |
|
|
|
GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); |
|
int result; |
|
try |
|
{ |
|
result = gzread(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); |
|
if (result < 0) |
|
throw new IOException(); |
|
} |
|
finally |
|
{ |
|
h.Free(); |
|
} |
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// Attempts to read a single byte from the stream. |
|
/// </summary> |
|
/// <returns>The byte that was read, or -1 in case of error or End-Of-File</returns> |
|
public override int ReadByte() |
|
{ |
|
if (!CanRead) throw new NotSupportedException(); |
|
if (_isDisposed) throw new ObjectDisposedException("GZipStream"); |
|
return gzgetc(_gzFile); |
|
} |
|
|
|
/// <summary> |
|
/// Writes a number of bytes to the stream |
|
/// </summary> |
|
/// <param name="buffer"></param> |
|
/// <param name="offset"></param> |
|
/// <param name="count"></param> |
|
/// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> |
|
/// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> |
|
/// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> |
|
/// <exception cref="NotSupportedException">If this stream is not writeable.</exception> |
|
/// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> |
|
public override void Write(byte[] buffer, int offset, int count) |
|
{ |
|
if (!CanWrite) throw new NotSupportedException(); |
|
if (buffer == null) throw new ArgumentNullException(); |
|
if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); |
|
if ((offset+count) > buffer.Length) throw new ArgumentException(); |
|
if (_isDisposed) throw new ObjectDisposedException("GZipStream"); |
|
|
|
GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); |
|
try |
|
{ |
|
int result = gzwrite(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); |
|
if (result < 0) |
|
throw new IOException(); |
|
} |
|
finally |
|
{ |
|
h.Free(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Writes a single byte to the stream |
|
/// </summary> |
|
/// <param name="value">The byte to add to the stream.</param> |
|
/// <exception cref="NotSupportedException">If this stream is not writeable.</exception> |
|
/// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> |
|
public override void WriteByte(byte value) |
|
{ |
|
if (!CanWrite) throw new NotSupportedException(); |
|
if (_isDisposed) throw new ObjectDisposedException("GZipStream"); |
|
|
|
int result = gzputc(_gzFile, (int)value); |
|
if (result < 0) |
|
throw new IOException(); |
|
} |
|
#endregion |
|
|
|
#region Position & length stuff |
|
/// <summary> |
|
/// Not supported. |
|
/// </summary> |
|
/// <param name="value"></param> |
|
/// <exception cref="NotSupportedException">Always thrown</exception> |
|
public override void SetLength(long value) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
|
|
/// <summary> |
|
/// Not suppported. |
|
/// </summary> |
|
/// <param name="offset"></param> |
|
/// <param name="origin"></param> |
|
/// <returns></returns> |
|
/// <exception cref="NotSupportedException">Always thrown</exception> |
|
public override long Seek(long offset, SeekOrigin origin) |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
|
|
/// <summary> |
|
/// Flushes the <c>GZipStream</c>. |
|
/// </summary> |
|
/// <remarks>In this implementation, this method does nothing. This is because excessive |
|
/// flushing may degrade the achievable compression rates.</remarks> |
|
public override void Flush() |
|
{ |
|
// left empty on purpose |
|
} |
|
|
|
/// <summary> |
|
/// Gets/sets the current position in the <c>GZipStream</c>. Not suppported. |
|
/// </summary> |
|
/// <remarks>In this implementation this property is not supported</remarks> |
|
/// <exception cref="NotSupportedException">Always thrown</exception> |
|
public override long Position |
|
{ |
|
get |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
set |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets the size of the stream. Not suppported. |
|
/// </summary> |
|
/// <remarks>In this implementation this property is not supported</remarks> |
|
/// <exception cref="NotSupportedException">Always thrown</exception> |
|
public override long Length |
|
{ |
|
get |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
#endregion |
|
} |
|
}
|
|
|