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