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