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 |
|
---|
8 | using System;
|
---|
9 | using System.IO;
|
---|
10 | using System.Runtime.InteropServices;
|
---|
11 |
|
---|
12 | namespace 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 > 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 > 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 | }
|
---|