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