Statistics
| Branch: | Revision:

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
}