| // | |
| // © 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 | |
| } | |
| } |