1*44bedb31SLionel Sambuc // 2*44bedb31SLionel Sambuc // � Copyright Henrik Ravn 2004 3*44bedb31SLionel Sambuc // 4*44bedb31SLionel Sambuc // Use, modification and distribution are subject to the Boost Software License, Version 1.0. 5*44bedb31SLionel Sambuc // (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6*44bedb31SLionel Sambuc // 7*44bedb31SLionel Sambuc 8*44bedb31SLionel Sambuc using System; 9*44bedb31SLionel Sambuc using System.IO; 10*44bedb31SLionel Sambuc using System.Runtime.InteropServices; 11*44bedb31SLionel Sambuc 12*44bedb31SLionel Sambuc namespace DotZLib 13*44bedb31SLionel Sambuc { 14*44bedb31SLionel Sambuc /// <summary> 15*44bedb31SLionel Sambuc /// Implements a compressed <see cref="Stream"/>, in GZip (.gz) format. 16*44bedb31SLionel Sambuc /// </summary> 17*44bedb31SLionel Sambuc public class GZipStream : Stream, IDisposable 18*44bedb31SLionel Sambuc { 19*44bedb31SLionel Sambuc #region Dll Imports 20*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi)] gzopen(string name, string mode)21*44bedb31SLionel Sambuc private static extern IntPtr gzopen(string name, string mode); 22*44bedb31SLionel Sambuc 23*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] gzclose(IntPtr gzFile)24*44bedb31SLionel Sambuc private static extern int gzclose(IntPtr gzFile); 25*44bedb31SLionel Sambuc 26*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] gzwrite(IntPtr gzFile, int data, int length)27*44bedb31SLionel Sambuc private static extern int gzwrite(IntPtr gzFile, int data, int length); 28*44bedb31SLionel Sambuc 29*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] gzread(IntPtr gzFile, int data, int length)30*44bedb31SLionel Sambuc private static extern int gzread(IntPtr gzFile, int data, int length); 31*44bedb31SLionel Sambuc 32*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] gzgetc(IntPtr gzFile)33*44bedb31SLionel Sambuc private static extern int gzgetc(IntPtr gzFile); 34*44bedb31SLionel Sambuc 35*44bedb31SLionel Sambuc [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)] gzputc(IntPtr gzFile, int c)36*44bedb31SLionel Sambuc private static extern int gzputc(IntPtr gzFile, int c); 37*44bedb31SLionel Sambuc 38*44bedb31SLionel Sambuc #endregion 39*44bedb31SLionel Sambuc 40*44bedb31SLionel Sambuc #region Private data 41*44bedb31SLionel Sambuc private IntPtr _gzFile; 42*44bedb31SLionel Sambuc private bool _isDisposed = false; 43*44bedb31SLionel Sambuc private bool _isWriting; 44*44bedb31SLionel Sambuc #endregion 45*44bedb31SLionel Sambuc 46*44bedb31SLionel Sambuc #region Constructors 47*44bedb31SLionel Sambuc /// <summary> 48*44bedb31SLionel Sambuc /// Creates a new file as a writeable GZipStream 49*44bedb31SLionel Sambuc /// </summary> 50*44bedb31SLionel Sambuc /// <param name="fileName">The name of the compressed file to create</param> 51*44bedb31SLionel Sambuc /// <param name="level">The compression level to use when adding data</param> 52*44bedb31SLionel Sambuc /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> GZipStream(string fileName, CompressLevel level)53*44bedb31SLionel Sambuc public GZipStream(string fileName, CompressLevel level) 54*44bedb31SLionel Sambuc { 55*44bedb31SLionel Sambuc _isWriting = true; 56*44bedb31SLionel Sambuc _gzFile = gzopen(fileName, String.Format("wb{0}", (int)level)); 57*44bedb31SLionel Sambuc if (_gzFile == IntPtr.Zero) 58*44bedb31SLionel Sambuc throw new ZLibException(-1, "Could not open " + fileName); 59*44bedb31SLionel Sambuc } 60*44bedb31SLionel Sambuc 61*44bedb31SLionel Sambuc /// <summary> 62*44bedb31SLionel Sambuc /// Opens an existing file as a readable GZipStream 63*44bedb31SLionel Sambuc /// </summary> 64*44bedb31SLionel Sambuc /// <param name="fileName">The name of the file to open</param> 65*44bedb31SLionel Sambuc /// <exception cref="ZLibException">If an error occurred in the internal zlib function</exception> GZipStream(string fileName)66*44bedb31SLionel Sambuc public GZipStream(string fileName) 67*44bedb31SLionel Sambuc { 68*44bedb31SLionel Sambuc _isWriting = false; 69*44bedb31SLionel Sambuc _gzFile = gzopen(fileName, "rb"); 70*44bedb31SLionel Sambuc if (_gzFile == IntPtr.Zero) 71*44bedb31SLionel Sambuc throw new ZLibException(-1, "Could not open " + fileName); 72*44bedb31SLionel Sambuc 73*44bedb31SLionel Sambuc } 74*44bedb31SLionel Sambuc #endregion 75*44bedb31SLionel Sambuc 76*44bedb31SLionel Sambuc #region Access properties 77*44bedb31SLionel Sambuc /// <summary> 78*44bedb31SLionel Sambuc /// Returns true of this stream can be read from, false otherwise 79*44bedb31SLionel Sambuc /// </summary> 80*44bedb31SLionel Sambuc public override bool CanRead 81*44bedb31SLionel Sambuc { 82*44bedb31SLionel Sambuc get 83*44bedb31SLionel Sambuc { 84*44bedb31SLionel Sambuc return !_isWriting; 85*44bedb31SLionel Sambuc } 86*44bedb31SLionel Sambuc } 87*44bedb31SLionel Sambuc 88*44bedb31SLionel Sambuc 89*44bedb31SLionel Sambuc /// <summary> 90*44bedb31SLionel Sambuc /// Returns false. 91*44bedb31SLionel Sambuc /// </summary> 92*44bedb31SLionel Sambuc public override bool CanSeek 93*44bedb31SLionel Sambuc { 94*44bedb31SLionel Sambuc get 95*44bedb31SLionel Sambuc { 96*44bedb31SLionel Sambuc return false; 97*44bedb31SLionel Sambuc } 98*44bedb31SLionel Sambuc } 99*44bedb31SLionel Sambuc 100*44bedb31SLionel Sambuc /// <summary> 101*44bedb31SLionel Sambuc /// Returns true if this tsream is writeable, false otherwise 102*44bedb31SLionel Sambuc /// </summary> 103*44bedb31SLionel Sambuc public override bool CanWrite 104*44bedb31SLionel Sambuc { 105*44bedb31SLionel Sambuc get 106*44bedb31SLionel Sambuc { 107*44bedb31SLionel Sambuc return _isWriting; 108*44bedb31SLionel Sambuc } 109*44bedb31SLionel Sambuc } 110*44bedb31SLionel Sambuc #endregion 111*44bedb31SLionel Sambuc 112*44bedb31SLionel Sambuc #region Destructor & IDispose stuff 113*44bedb31SLionel Sambuc 114*44bedb31SLionel Sambuc /// <summary> 115*44bedb31SLionel Sambuc /// Destroys this instance 116*44bedb31SLionel Sambuc /// </summary> ~GZipStream()117*44bedb31SLionel Sambuc ~GZipStream() 118*44bedb31SLionel Sambuc { 119*44bedb31SLionel Sambuc cleanUp(false); 120*44bedb31SLionel Sambuc } 121*44bedb31SLionel Sambuc 122*44bedb31SLionel Sambuc /// <summary> 123*44bedb31SLionel Sambuc /// Closes the external file handle 124*44bedb31SLionel Sambuc /// </summary> Dispose()125*44bedb31SLionel Sambuc public void Dispose() 126*44bedb31SLionel Sambuc { 127*44bedb31SLionel Sambuc cleanUp(true); 128*44bedb31SLionel Sambuc } 129*44bedb31SLionel Sambuc 130*44bedb31SLionel Sambuc // Does the actual closing of the file handle. cleanUp(bool isDisposing)131*44bedb31SLionel Sambuc private void cleanUp(bool isDisposing) 132*44bedb31SLionel Sambuc { 133*44bedb31SLionel Sambuc if (!_isDisposed) 134*44bedb31SLionel Sambuc { 135*44bedb31SLionel Sambuc gzclose(_gzFile); 136*44bedb31SLionel Sambuc _isDisposed = true; 137*44bedb31SLionel Sambuc } 138*44bedb31SLionel Sambuc } 139*44bedb31SLionel Sambuc #endregion 140*44bedb31SLionel Sambuc 141*44bedb31SLionel Sambuc #region Basic reading and writing 142*44bedb31SLionel Sambuc /// <summary> 143*44bedb31SLionel Sambuc /// Attempts to read a number of bytes from the stream. 144*44bedb31SLionel Sambuc /// </summary> 145*44bedb31SLionel Sambuc /// <param name="buffer">The destination data buffer</param> 146*44bedb31SLionel Sambuc /// <param name="offset">The index of the first destination byte in <c>buffer</c></param> 147*44bedb31SLionel Sambuc /// <param name="count">The number of bytes requested</param> 148*44bedb31SLionel Sambuc /// <returns>The number of bytes read</returns> 149*44bedb31SLionel Sambuc /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> 150*44bedb31SLionel Sambuc /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> 151*44bedb31SLionel Sambuc /// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> 152*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">If this stream is not readable.</exception> 153*44bedb31SLionel Sambuc /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> Read(byte[] buffer, int offset, int count)154*44bedb31SLionel Sambuc public override int Read(byte[] buffer, int offset, int count) 155*44bedb31SLionel Sambuc { 156*44bedb31SLionel Sambuc if (!CanRead) throw new NotSupportedException(); 157*44bedb31SLionel Sambuc if (buffer == null) throw new ArgumentNullException(); 158*44bedb31SLionel Sambuc if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); 159*44bedb31SLionel Sambuc if ((offset+count) > buffer.Length) throw new ArgumentException(); 160*44bedb31SLionel Sambuc if (_isDisposed) throw new ObjectDisposedException("GZipStream"); 161*44bedb31SLionel Sambuc 162*44bedb31SLionel Sambuc GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); 163*44bedb31SLionel Sambuc int result; 164*44bedb31SLionel Sambuc try 165*44bedb31SLionel Sambuc { 166*44bedb31SLionel Sambuc result = gzread(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); 167*44bedb31SLionel Sambuc if (result < 0) 168*44bedb31SLionel Sambuc throw new IOException(); 169*44bedb31SLionel Sambuc } 170*44bedb31SLionel Sambuc finally 171*44bedb31SLionel Sambuc { 172*44bedb31SLionel Sambuc h.Free(); 173*44bedb31SLionel Sambuc } 174*44bedb31SLionel Sambuc return result; 175*44bedb31SLionel Sambuc } 176*44bedb31SLionel Sambuc 177*44bedb31SLionel Sambuc /// <summary> 178*44bedb31SLionel Sambuc /// Attempts to read a single byte from the stream. 179*44bedb31SLionel Sambuc /// </summary> 180*44bedb31SLionel Sambuc /// <returns>The byte that was read, or -1 in case of error or End-Of-File</returns> ReadByte()181*44bedb31SLionel Sambuc public override int ReadByte() 182*44bedb31SLionel Sambuc { 183*44bedb31SLionel Sambuc if (!CanRead) throw new NotSupportedException(); 184*44bedb31SLionel Sambuc if (_isDisposed) throw new ObjectDisposedException("GZipStream"); 185*44bedb31SLionel Sambuc return gzgetc(_gzFile); 186*44bedb31SLionel Sambuc } 187*44bedb31SLionel Sambuc 188*44bedb31SLionel Sambuc /// <summary> 189*44bedb31SLionel Sambuc /// Writes a number of bytes to the stream 190*44bedb31SLionel Sambuc /// </summary> 191*44bedb31SLionel Sambuc /// <param name="buffer"></param> 192*44bedb31SLionel Sambuc /// <param name="offset"></param> 193*44bedb31SLionel Sambuc /// <param name="count"></param> 194*44bedb31SLionel Sambuc /// <exception cref="ArgumentNullException">If <c>buffer</c> is null</exception> 195*44bedb31SLionel Sambuc /// <exception cref="ArgumentOutOfRangeException">If <c>count</c> or <c>offset</c> are negative</exception> 196*44bedb31SLionel Sambuc /// <exception cref="ArgumentException">If <c>offset</c> + <c>count</c> is > buffer.Length</exception> 197*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">If this stream is not writeable.</exception> 198*44bedb31SLionel Sambuc /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> Write(byte[] buffer, int offset, int count)199*44bedb31SLionel Sambuc public override void Write(byte[] buffer, int offset, int count) 200*44bedb31SLionel Sambuc { 201*44bedb31SLionel Sambuc if (!CanWrite) throw new NotSupportedException(); 202*44bedb31SLionel Sambuc if (buffer == null) throw new ArgumentNullException(); 203*44bedb31SLionel Sambuc if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException(); 204*44bedb31SLionel Sambuc if ((offset+count) > buffer.Length) throw new ArgumentException(); 205*44bedb31SLionel Sambuc if (_isDisposed) throw new ObjectDisposedException("GZipStream"); 206*44bedb31SLionel Sambuc 207*44bedb31SLionel Sambuc GCHandle h = GCHandle.Alloc(buffer, GCHandleType.Pinned); 208*44bedb31SLionel Sambuc try 209*44bedb31SLionel Sambuc { 210*44bedb31SLionel Sambuc int result = gzwrite(_gzFile, h.AddrOfPinnedObject().ToInt32() + offset, count); 211*44bedb31SLionel Sambuc if (result < 0) 212*44bedb31SLionel Sambuc throw new IOException(); 213*44bedb31SLionel Sambuc } 214*44bedb31SLionel Sambuc finally 215*44bedb31SLionel Sambuc { 216*44bedb31SLionel Sambuc h.Free(); 217*44bedb31SLionel Sambuc } 218*44bedb31SLionel Sambuc } 219*44bedb31SLionel Sambuc 220*44bedb31SLionel Sambuc /// <summary> 221*44bedb31SLionel Sambuc /// Writes a single byte to the stream 222*44bedb31SLionel Sambuc /// </summary> 223*44bedb31SLionel Sambuc /// <param name="value">The byte to add to the stream.</param> 224*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">If this stream is not writeable.</exception> 225*44bedb31SLionel Sambuc /// <exception cref="ObjectDisposedException">If this stream has been disposed.</exception> WriteByte(byte value)226*44bedb31SLionel Sambuc public override void WriteByte(byte value) 227*44bedb31SLionel Sambuc { 228*44bedb31SLionel Sambuc if (!CanWrite) throw new NotSupportedException(); 229*44bedb31SLionel Sambuc if (_isDisposed) throw new ObjectDisposedException("GZipStream"); 230*44bedb31SLionel Sambuc 231*44bedb31SLionel Sambuc int result = gzputc(_gzFile, (int)value); 232*44bedb31SLionel Sambuc if (result < 0) 233*44bedb31SLionel Sambuc throw new IOException(); 234*44bedb31SLionel Sambuc } 235*44bedb31SLionel Sambuc #endregion 236*44bedb31SLionel Sambuc 237*44bedb31SLionel Sambuc #region Position & length stuff 238*44bedb31SLionel Sambuc /// <summary> 239*44bedb31SLionel Sambuc /// Not supported. 240*44bedb31SLionel Sambuc /// </summary> 241*44bedb31SLionel Sambuc /// <param name="value"></param> 242*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">Always thrown</exception> SetLength(long value)243*44bedb31SLionel Sambuc public override void SetLength(long value) 244*44bedb31SLionel Sambuc { 245*44bedb31SLionel Sambuc throw new NotSupportedException(); 246*44bedb31SLionel Sambuc } 247*44bedb31SLionel Sambuc 248*44bedb31SLionel Sambuc /// <summary> 249*44bedb31SLionel Sambuc /// Not suppported. 250*44bedb31SLionel Sambuc /// </summary> 251*44bedb31SLionel Sambuc /// <param name="offset"></param> 252*44bedb31SLionel Sambuc /// <param name="origin"></param> 253*44bedb31SLionel Sambuc /// <returns></returns> 254*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">Always thrown</exception> Seek(long offset, SeekOrigin origin)255*44bedb31SLionel Sambuc public override long Seek(long offset, SeekOrigin origin) 256*44bedb31SLionel Sambuc { 257*44bedb31SLionel Sambuc throw new NotSupportedException(); 258*44bedb31SLionel Sambuc } 259*44bedb31SLionel Sambuc 260*44bedb31SLionel Sambuc /// <summary> 261*44bedb31SLionel Sambuc /// Flushes the <c>GZipStream</c>. 262*44bedb31SLionel Sambuc /// </summary> 263*44bedb31SLionel Sambuc /// <remarks>In this implementation, this method does nothing. This is because excessive 264*44bedb31SLionel Sambuc /// flushing may degrade the achievable compression rates.</remarks> Flush()265*44bedb31SLionel Sambuc public override void Flush() 266*44bedb31SLionel Sambuc { 267*44bedb31SLionel Sambuc // left empty on purpose 268*44bedb31SLionel Sambuc } 269*44bedb31SLionel Sambuc 270*44bedb31SLionel Sambuc /// <summary> 271*44bedb31SLionel Sambuc /// Gets/sets the current position in the <c>GZipStream</c>. Not suppported. 272*44bedb31SLionel Sambuc /// </summary> 273*44bedb31SLionel Sambuc /// <remarks>In this implementation this property is not supported</remarks> 274*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">Always thrown</exception> 275*44bedb31SLionel Sambuc public override long Position 276*44bedb31SLionel Sambuc { 277*44bedb31SLionel Sambuc get 278*44bedb31SLionel Sambuc { 279*44bedb31SLionel Sambuc throw new NotSupportedException(); 280*44bedb31SLionel Sambuc } 281*44bedb31SLionel Sambuc set 282*44bedb31SLionel Sambuc { 283*44bedb31SLionel Sambuc throw new NotSupportedException(); 284*44bedb31SLionel Sambuc } 285*44bedb31SLionel Sambuc } 286*44bedb31SLionel Sambuc 287*44bedb31SLionel Sambuc /// <summary> 288*44bedb31SLionel Sambuc /// Gets the size of the stream. Not suppported. 289*44bedb31SLionel Sambuc /// </summary> 290*44bedb31SLionel Sambuc /// <remarks>In this implementation this property is not supported</remarks> 291*44bedb31SLionel Sambuc /// <exception cref="NotSupportedException">Always thrown</exception> 292*44bedb31SLionel Sambuc public override long Length 293*44bedb31SLionel Sambuc { 294*44bedb31SLionel Sambuc get 295*44bedb31SLionel Sambuc { 296*44bedb31SLionel Sambuc throw new NotSupportedException(); 297*44bedb31SLionel Sambuc } 298*44bedb31SLionel Sambuc } 299*44bedb31SLionel Sambuc #endregion 300*44bedb31SLionel Sambuc } 301*44bedb31SLionel Sambuc } 302