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