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