root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / Compression / Streams / DeflaterOutputStream.cs @ 0eea575a
History | View | Annotate | Download (17.2 kB)
1 |
// DeflaterOutputStream.cs |
---|---|
2 |
// |
3 |
// Copyright (C) 2001 Mike Krueger |
4 |
// |
5 |
// This file was translated from java, it was part of the GNU Classpath |
6 |
// Copyright (C) 2001 Free Software Foundation, Inc. |
7 |
// |
8 |
// This program is free software; you can redistribute it and/or |
9 |
// modify it under the terms of the GNU General Public License |
10 |
// as published by the Free Software Foundation; either version 2 |
11 |
// of the License, or (at your option) any later version. |
12 |
// |
13 |
// This program is distributed in the hope that it will be useful, |
14 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
// GNU General Public License for more details. |
17 |
// |
18 |
// You should have received a copy of the GNU General Public License |
19 |
// along with this program; if not, write to the Free Software |
20 |
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 |
// |
22 |
// Linking this library statically or dynamically with other modules is |
23 |
// making a combined work based on this library. Thus, the terms and |
24 |
// conditions of the GNU General Public License cover the whole |
25 |
// combination. |
26 |
// |
27 |
// As a special exception, the copyright holders of this library give you |
28 |
// permission to link this library with independent modules to produce an |
29 |
// executable, regardless of the license terms of these independent |
30 |
// modules, and to copy and distribute the resulting executable under |
31 |
// terms of your choice, provided that you also meet, for each linked |
32 |
// independent module, the terms and conditions of the license of that |
33 |
// module. An independent module is a module which is not derived from |
34 |
// or based on this library. If you modify this library, you may extend |
35 |
// this exception to your version of the library, but you are not |
36 |
// obligated to do so. If you do not wish to do so, delete this |
37 |
// exception statement from your version. |
38 |
|
39 |
using System; |
40 |
using System.IO; |
41 |
using System.Security.Cryptography; |
42 |
using ICSharpCode.SharpZipLib.Silverlight.Encryption; |
43 |
|
44 |
namespace ICSharpCode.SharpZipLib.Silverlight.Zip.Compression.Streams |
45 |
{ |
46 |
/// <summary> |
47 |
/// A special stream deflating or compressing the bytes that are |
48 |
/// written to it. It uses a Deflater to perform actual deflating.<br/> |
49 |
/// Authors of the original java version : Tom Tromey, Jochen Hoenicke |
50 |
/// </summary> |
51 |
public class DeflaterOutputStream : Stream |
52 |
{ |
53 |
#region Constructors |
54 |
|
55 |
/// <summary> |
56 |
/// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. |
57 |
/// </summary> |
58 |
/// <param name="baseOutputStream"> |
59 |
/// the output stream where deflated output should be written. |
60 |
/// </param> |
61 |
public DeflaterOutputStream(Stream baseOutputStream) |
62 |
: this(baseOutputStream, new Deflater(), 512) |
63 |
{ |
64 |
} |
65 |
|
66 |
/// <summary> |
67 |
/// Creates a new DeflaterOutputStream with the given Deflater and |
68 |
/// default buffer size. |
69 |
/// </summary> |
70 |
/// <param name="baseOutputStream"> |
71 |
/// the output stream where deflated output should be written. |
72 |
/// </param> |
73 |
/// <param name="deflater"> |
74 |
/// the underlying deflater. |
75 |
/// </param> |
76 |
public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) |
77 |
: this(baseOutputStream, deflater, 512) |
78 |
{ |
79 |
} |
80 |
|
81 |
/// <summary> |
82 |
/// Creates a new DeflaterOutputStream with the given Deflater and |
83 |
/// buffer size. |
84 |
/// </summary> |
85 |
/// <param name="baseOutputStream"> |
86 |
/// The output stream where deflated output is written. |
87 |
/// </param> |
88 |
/// <param name="deflater"> |
89 |
/// The underlying deflater to use |
90 |
/// </param> |
91 |
/// <param name="bufferSize"> |
92 |
/// The buffer size to use when deflating |
93 |
/// </param> |
94 |
/// <exception cref="ArgumentOutOfRangeException"> |
95 |
/// bufsize is less than or equal to zero. |
96 |
/// </exception> |
97 |
/// <exception cref="ArgumentException"> |
98 |
/// baseOutputStream does not support writing |
99 |
/// </exception> |
100 |
/// <exception cref="ArgumentNullException"> |
101 |
/// deflater instance is null |
102 |
/// </exception> |
103 |
public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) |
104 |
{ |
105 |
if (baseOutputStream == null) |
106 |
{ |
107 |
throw new ArgumentNullException("baseOutputStream"); |
108 |
} |
109 |
|
110 |
if (baseOutputStream.CanWrite == false) |
111 |
{ |
112 |
throw new ArgumentException("Must support writing", "baseOutputStream"); |
113 |
} |
114 |
|
115 |
if (deflater == null) |
116 |
{ |
117 |
throw new ArgumentNullException("deflater"); |
118 |
} |
119 |
|
120 |
if (bufferSize <= 0) |
121 |
{ |
122 |
throw new ArgumentOutOfRangeException("bufferSize"); |
123 |
} |
124 |
|
125 |
baseOutputStream_ = baseOutputStream; |
126 |
buffer_ = new byte[bufferSize]; |
127 |
deflater_ = deflater; |
128 |
} |
129 |
|
130 |
#endregion |
131 |
|
132 |
#region Public API |
133 |
|
134 |
/// <summary> |
135 |
/// Get/set flag indicating ownership of the underlying stream. |
136 |
/// When the flag is true <see cref="Close"></see> will close the underlying stream also. |
137 |
/// </summary> |
138 |
public bool IsStreamOwner |
139 |
{ |
140 |
get { return isStreamOwner_; } |
141 |
set { isStreamOwner_ = value; } |
142 |
} |
143 |
|
144 |
/// <summary> |
145 |
/// Allows client to determine if an entry can be patched after its added |
146 |
/// </summary> |
147 |
public bool CanPatchEntries |
148 |
{ |
149 |
get { return baseOutputStream_.CanSeek; } |
150 |
} |
151 |
|
152 |
/// <summary> |
153 |
/// Finishes the stream by calling finish() on the deflater. |
154 |
/// </summary> |
155 |
/// <exception cref="SharpZipBaseException"> |
156 |
/// Not all input is deflated |
157 |
/// </exception> |
158 |
public virtual void Finish() |
159 |
{ |
160 |
deflater_.Finish(); |
161 |
while (!deflater_.IsFinished) |
162 |
{ |
163 |
var len = deflater_.Deflate(buffer_, 0, buffer_.Length); |
164 |
if (len <= 0) |
165 |
{ |
166 |
break; |
167 |
} |
168 |
if (cryptoTransform_ != null) |
169 |
{ |
170 |
EncryptBlock(buffer_, 0, len); |
171 |
} |
172 |
|
173 |
baseOutputStream_.Write(buffer_, 0, len); |
174 |
} |
175 |
|
176 |
if (!deflater_.IsFinished) |
177 |
{ |
178 |
throw new SharpZipBaseException("Can't deflate all input?"); |
179 |
} |
180 |
|
181 |
baseOutputStream_.Flush(); |
182 |
|
183 |
if (cryptoTransform_ == null) |
184 |
{ |
185 |
return; |
186 |
} |
187 |
cryptoTransform_.Dispose(); |
188 |
cryptoTransform_ = null; |
189 |
} |
190 |
|
191 |
#endregion |
192 |
|
193 |
#region Encryption |
194 |
|
195 |
private ICryptoTransform cryptoTransform_; |
196 |
private string _password; |
197 |
|
198 |
/// <summary> |
199 |
/// Get/set the password used for encryption. |
200 |
/// </summary> |
201 |
/// <remarks>When set to null or if the password is empty no encryption is performed</remarks> |
202 |
public string Password |
203 |
{ |
204 |
get { return _password; } |
205 |
set |
206 |
{ |
207 |
if ((value != null) && (value.Length == 0)) |
208 |
{ |
209 |
_password = null; |
210 |
} |
211 |
else |
212 |
{ |
213 |
_password = value; |
214 |
} |
215 |
} |
216 |
} |
217 |
|
218 |
/// <summary> |
219 |
/// Encrypt a block of data |
220 |
/// </summary> |
221 |
/// <param name="buffer"> |
222 |
/// Data to encrypt. NOTE the original contents of the buffer are lost |
223 |
/// </param> |
224 |
/// <param name="offset"> |
225 |
/// Offset of first byte in buffer to encrypt |
226 |
/// </param> |
227 |
/// <param name="length"> |
228 |
/// Number of bytes in buffer to encrypt |
229 |
/// </param> |
230 |
protected void EncryptBlock(byte[] buffer, int offset, int length) |
231 |
{ |
232 |
cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); |
233 |
} |
234 |
|
235 |
/// <summary> |
236 |
/// Initializes encryption keys based on given password |
237 |
/// </summary> |
238 |
/// <param name="password">The password.</param> |
239 |
protected void InitializePassword(string password) |
240 |
{ |
241 |
var pkManaged = new PkzipClassicManaged(); |
242 |
var key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password)); |
243 |
cryptoTransform_ = pkManaged.CreateEncryptor(key, null); |
244 |
} |
245 |
|
246 |
#endregion |
247 |
|
248 |
#region Deflation Support |
249 |
|
250 |
/// <summary> |
251 |
/// Deflates everything in the input buffers. This will call |
252 |
/// <code>def.deflate()</code> until all bytes from the input buffers |
253 |
/// are processed. |
254 |
/// </summary> |
255 |
protected void Deflate() |
256 |
{ |
257 |
while (!deflater_.IsNeedingInput) |
258 |
{ |
259 |
var deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); |
260 |
|
261 |
if (deflateCount <= 0) |
262 |
{ |
263 |
break; |
264 |
} |
265 |
if (cryptoTransform_ != null) |
266 |
{ |
267 |
EncryptBlock(buffer_, 0, deflateCount); |
268 |
} |
269 |
|
270 |
baseOutputStream_.Write(buffer_, 0, deflateCount); |
271 |
} |
272 |
|
273 |
if (!deflater_.IsNeedingInput) |
274 |
{ |
275 |
throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); |
276 |
} |
277 |
} |
278 |
|
279 |
#endregion |
280 |
|
281 |
#region Stream Overrides |
282 |
|
283 |
/// <summary> |
284 |
/// Gets value indicating stream can be read from |
285 |
/// </summary> |
286 |
public override bool CanRead |
287 |
{ |
288 |
get { return false; } |
289 |
} |
290 |
|
291 |
/// <summary> |
292 |
/// Gets a value indicating if seeking is supported for this stream |
293 |
/// This property always returns false |
294 |
/// </summary> |
295 |
public override bool CanSeek |
296 |
{ |
297 |
get { return false; } |
298 |
} |
299 |
|
300 |
/// <summary> |
301 |
/// Get value indicating if this stream supports writing |
302 |
/// </summary> |
303 |
public override bool CanWrite |
304 |
{ |
305 |
get { return baseOutputStream_.CanWrite; } |
306 |
} |
307 |
|
308 |
/// <summary> |
309 |
/// Get current length of stream |
310 |
/// </summary> |
311 |
public override long Length |
312 |
{ |
313 |
get { return baseOutputStream_.Length; } |
314 |
} |
315 |
|
316 |
/// <summary> |
317 |
/// Gets the current position within the stream. |
318 |
/// </summary> |
319 |
/// <exception cref="NotSupportedException">Any attempt to set position</exception> |
320 |
public override long Position |
321 |
{ |
322 |
get { return baseOutputStream_.Position; } |
323 |
set { throw new NotSupportedException("Position property not supported"); } |
324 |
} |
325 |
|
326 |
/// <summary> |
327 |
/// Sets the current position of this stream to the given value. Not supported by this class! |
328 |
/// </summary> |
329 |
/// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param> |
330 |
/// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param> |
331 |
/// <returns>The new position in the stream.</returns> |
332 |
/// <exception cref="NotSupportedException">Any access</exception> |
333 |
public override long Seek(long offset, SeekOrigin origin) |
334 |
{ |
335 |
throw new NotSupportedException("DeflaterOutputStream Seek not supported"); |
336 |
} |
337 |
|
338 |
/// <summary> |
339 |
/// Sets the length of this stream to the given value. Not supported by this class! |
340 |
/// </summary> |
341 |
/// <param name="value">The new stream length.</param> |
342 |
/// <exception cref="NotSupportedException">Any access</exception> |
343 |
public override void SetLength(long value) |
344 |
{ |
345 |
throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); |
346 |
} |
347 |
|
348 |
/// <summary> |
349 |
/// Read a byte from stream advancing position by one |
350 |
/// </summary> |
351 |
/// <returns>The byte read cast to an int. THe value is -1 if at the end of the stream.</returns> |
352 |
/// <exception cref="NotSupportedException">Any access</exception> |
353 |
public override int ReadByte() |
354 |
{ |
355 |
throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); |
356 |
} |
357 |
|
358 |
/// <summary> |
359 |
/// Read a block of bytes from stream |
360 |
/// </summary> |
361 |
/// <param name="buffer">The buffer to store read data in.</param> |
362 |
/// <param name="offset">The offset to start storing at.</param> |
363 |
/// <param name="count">The maximum number of bytes to read.</param> |
364 |
/// <returns>The actual number of bytes read. Zero if end of stream is detected.</returns> |
365 |
/// <exception cref="NotSupportedException">Any access</exception> |
366 |
public override int Read(byte[] buffer, int offset, int count) |
367 |
{ |
368 |
throw new NotSupportedException("DeflaterOutputStream Read not supported"); |
369 |
} |
370 |
|
371 |
/// <summary> |
372 |
/// Asynchronous reads are not supported a NotSupportedException is always thrown |
373 |
/// </summary> |
374 |
/// <param name="buffer">The buffer to read into.</param> |
375 |
/// <param name="offset">The offset to start storing data at.</param> |
376 |
/// <param name="count">The number of bytes to read</param> |
377 |
/// <param name="callback">The async callback to use.</param> |
378 |
/// <param name="state">The state to use.</param> |
379 |
/// <returns>Returns an <see cref="IAsyncResult"/></returns> |
380 |
/// <exception cref="NotSupportedException">Any access</exception> |
381 |
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, |
382 |
object state) |
383 |
{ |
384 |
throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported"); |
385 |
} |
386 |
|
387 |
/// <summary> |
388 |
/// Asynchronous writes arent supported, a NotSupportedException is always thrown |
389 |
/// </summary> |
390 |
/// <param name="buffer">The buffer to write.</param> |
391 |
/// <param name="offset">The offset to begin writing at.</param> |
392 |
/// <param name="count">The number of bytes to write.</param> |
393 |
/// <param name="callback">The <see cref="AsyncCallback"/> to use.</param> |
394 |
/// <param name="state">The state object.</param> |
395 |
/// <returns>Returns an IAsyncResult.</returns> |
396 |
/// <exception cref="NotSupportedException">Any access</exception> |
397 |
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, |
398 |
object state) |
399 |
{ |
400 |
throw new NotSupportedException("BeginWrite is not supported"); |
401 |
} |
402 |
|
403 |
/// <summary> |
404 |
/// Flushes the stream by calling <see cref="DeflaterOutputStream.Flush">Flush</see> on the deflater and then |
405 |
/// on the underlying stream. This ensures that all bytes are flushed. |
406 |
/// </summary> |
407 |
public override void Flush() |
408 |
{ |
409 |
deflater_.Flush(); |
410 |
Deflate(); |
411 |
baseOutputStream_.Flush(); |
412 |
} |
413 |
|
414 |
/// <summary> |
415 |
/// Calls <see cref="Finish"/> and closes the underlying |
416 |
/// stream when <see cref="IsStreamOwner"></see> is true. |
417 |
/// </summary> |
418 |
public override void Close() |
419 |
{ |
420 |
if (!isClosed_) |
421 |
{ |
422 |
isClosed_ = true; |
423 |
|
424 |
try |
425 |
{ |
426 |
Finish(); |
427 |
if (cryptoTransform_ != null) |
428 |
{ |
429 |
cryptoTransform_.Dispose(); |
430 |
cryptoTransform_ = null; |
431 |
} |
432 |
} |
433 |
finally |
434 |
{ |
435 |
if (isStreamOwner_) |
436 |
{ |
437 |
baseOutputStream_.Close(); |
438 |
} |
439 |
} |
440 |
} |
441 |
} |
442 |
|
443 |
/// <summary> |
444 |
/// Writes a single byte to the compressed output stream. |
445 |
/// </summary> |
446 |
/// <param name="value"> |
447 |
/// The byte value. |
448 |
/// </param> |
449 |
public override void WriteByte(byte value) |
450 |
{ |
451 |
var b = new byte[1]; |
452 |
b[0] = value; |
453 |
Write(b, 0, 1); |
454 |
} |
455 |
|
456 |
/// <summary> |
457 |
/// Writes bytes from an array to the compressed stream. |
458 |
/// </summary> |
459 |
/// <param name="buffer"> |
460 |
/// The byte array |
461 |
/// </param> |
462 |
/// <param name="offset"> |
463 |
/// The offset into the byte array where to start. |
464 |
/// </param> |
465 |
/// <param name="count"> |
466 |
/// The number of bytes to write. |
467 |
/// </param> |
468 |
public override void Write(byte[] buffer, int offset, int count) |
469 |
{ |
470 |
deflater_.SetInput(buffer, offset, count); |
471 |
Deflate(); |
472 |
} |
473 |
|
474 |
#endregion |
475 |
|
476 |
#region Instance Fields |
477 |
|
478 |
/// <summary> |
479 |
/// This buffer is used temporarily to retrieve the bytes from the |
480 |
/// deflater and write them to the underlying output stream. |
481 |
/// </summary> |
482 |
private readonly byte[] buffer_; |
483 |
|
484 |
/// <summary> |
485 |
/// Base stream the deflater depends on. |
486 |
/// </summary> |
487 |
protected Stream baseOutputStream_; |
488 |
|
489 |
/// <summary> |
490 |
/// The deflater which is used to deflate the stream. |
491 |
/// </summary> |
492 |
protected Deflater deflater_; |
493 |
|
494 |
private bool isClosed_; |
495 |
|
496 |
private bool isStreamOwner_ = true; |
497 |
|
498 |
#endregion |
499 |
} |
500 |
} |