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