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