302 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			302 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|   | // | |||
|  | // <20> 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 | |||
|  |     } | |||
|  | } |