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