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