comparison Zip/Compression/Streams/InflaterInputStream.cs @ 1:94e25b786321

Re #311: can't read ZIP file packed by Linux app Archive Manager/File Roller Initial commit of clean SharpZipLib 0860 source. Only change is build paths.
author IBBoard <dev@ibboard.co.uk>
date Sat, 30 Oct 2010 14:03:17 +0000
parents
children
comparison
equal deleted inserted replaced
0:d16ef17fa7bb 1:94e25b786321
1 // InflaterInputStream.cs
2 //
3 // Copyright (C) 2001 Mike Krueger
4 // Copyright (C) 2004 John Reilly
5 //
6 // This file was translated from java, it was part of the GNU Classpath
7 // Copyright (C) 2001 Free Software Foundation, Inc.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 //
23 // Linking this library statically or dynamically with other modules is
24 // making a combined work based on this library. Thus, the terms and
25 // conditions of the GNU General Public License cover the whole
26 // combination.
27 //
28 // As a special exception, the copyright holders of this library give you
29 // permission to link this library with independent modules to produce an
30 // executable, regardless of the license terms of these independent
31 // modules, and to copy and distribute the resulting executable under
32 // terms of your choice, provided that you also meet, for each linked
33 // independent module, the terms and conditions of the license of that
34 // module. An independent module is a module which is not derived from
35 // or based on this library. If you modify this library, you may extend
36 // this exception to your version of the library, but you are not
37 // obligated to do so. If you do not wish to do so, delete this
38 // exception statement from your version.
39
40 // HISTORY
41 // 11-08-2009 GeoffHart T9121 Added Multi-member gzip support
42
43 using System;
44 using System.IO;
45
46 #if !NETCF_1_0
47 using System.Security.Cryptography;
48 #endif
49
50 namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
51 {
52
53 /// <summary>
54 /// An input buffer customised for use by <see cref="InflaterInputStream"/>
55 /// </summary>
56 /// <remarks>
57 /// The buffer supports decryption of incoming data.
58 /// </remarks>
59 public class InflaterInputBuffer
60 {
61 #region Constructors
62 /// <summary>
63 /// Initialise a new instance of <see cref="InflaterInputBuffer"/> with a default buffer size
64 /// </summary>
65 /// <param name="stream">The stream to buffer.</param>
66 public InflaterInputBuffer(Stream stream) : this(stream , 4096)
67 {
68 }
69
70 /// <summary>
71 /// Initialise a new instance of <see cref="InflaterInputBuffer"/>
72 /// </summary>
73 /// <param name="stream">The stream to buffer.</param>
74 /// <param name="bufferSize">The size to use for the buffer</param>
75 /// <remarks>A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB.</remarks>
76 public InflaterInputBuffer(Stream stream, int bufferSize)
77 {
78 inputStream = stream;
79 if ( bufferSize < 1024 ) {
80 bufferSize = 1024;
81 }
82 rawData = new byte[bufferSize];
83 clearText = rawData;
84 }
85 #endregion
86
87 /// <summary>
88 /// Get the length of bytes bytes in the <see cref="RawData"/>
89 /// </summary>
90 public int RawLength
91 {
92 get {
93 return rawLength;
94 }
95 }
96
97 /// <summary>
98 /// Get the contents of the raw data buffer.
99 /// </summary>
100 /// <remarks>This may contain encrypted data.</remarks>
101 public byte[] RawData
102 {
103 get {
104 return rawData;
105 }
106 }
107
108 /// <summary>
109 /// Get the number of useable bytes in <see cref="ClearText"/>
110 /// </summary>
111 public int ClearTextLength
112 {
113 get {
114 return clearTextLength;
115 }
116 }
117
118 /// <summary>
119 /// Get the contents of the clear text buffer.
120 /// </summary>
121 public byte[] ClearText
122 {
123 get {
124 return clearText;
125 }
126 }
127
128 /// <summary>
129 /// Get/set the number of bytes available
130 /// </summary>
131 public int Available
132 {
133 get { return available; }
134 set { available = value; }
135 }
136
137 /// <summary>
138 /// Call <see cref="Inflater.SetInput(byte[], int, int)"/> passing the current clear text buffer contents.
139 /// </summary>
140 /// <param name="inflater">The inflater to set input for.</param>
141 public void SetInflaterInput(Inflater inflater)
142 {
143 if ( available > 0 ) {
144 inflater.SetInput(clearText, clearTextLength - available, available);
145 available = 0;
146 }
147 }
148
149 /// <summary>
150 /// Fill the buffer from the underlying input stream.
151 /// </summary>
152 public void Fill()
153 {
154 rawLength = 0;
155 int toRead = rawData.Length;
156
157 while (toRead > 0) {
158 int count = inputStream.Read(rawData, rawLength, toRead);
159 if ( count <= 0 ) {
160 break;
161 }
162 rawLength += count;
163 toRead -= count;
164 }
165
166 #if !NETCF_1_0
167 if ( cryptoTransform != null ) {
168 clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
169 }
170 else
171 #endif
172 {
173 clearTextLength = rawLength;
174 }
175
176 available = clearTextLength;
177 }
178
179 /// <summary>
180 /// Read a buffer directly from the input stream
181 /// </summary>
182 /// <param name="buffer">The buffer to fill</param>
183 /// <returns>Returns the number of bytes read.</returns>
184 public int ReadRawBuffer(byte[] buffer)
185 {
186 return ReadRawBuffer(buffer, 0, buffer.Length);
187 }
188
189 /// <summary>
190 /// Read a buffer directly from the input stream
191 /// </summary>
192 /// <param name="outBuffer">The buffer to read into</param>
193 /// <param name="offset">The offset to start reading data into.</param>
194 /// <param name="length">The number of bytes to read.</param>
195 /// <returns>Returns the number of bytes read.</returns>
196 public int ReadRawBuffer(byte[] outBuffer, int offset, int length)
197 {
198 if ( length < 0 ) {
199 throw new ArgumentOutOfRangeException("length");
200 }
201
202 int currentOffset = offset;
203 int currentLength = length;
204
205 while ( currentLength > 0 ) {
206 if ( available <= 0 ) {
207 Fill();
208 if (available <= 0) {
209 return 0;
210 }
211 }
212 int toCopy = Math.Min(currentLength, available);
213 System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
214 currentOffset += toCopy;
215 currentLength -= toCopy;
216 available -= toCopy;
217 }
218 return length;
219 }
220
221 /// <summary>
222 /// Read clear text data from the input stream.
223 /// </summary>
224 /// <param name="outBuffer">The buffer to add data to.</param>
225 /// <param name="offset">The offset to start adding data at.</param>
226 /// <param name="length">The number of bytes to read.</param>
227 /// <returns>Returns the number of bytes actually read.</returns>
228 public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
229 {
230 if ( length < 0 ) {
231 throw new ArgumentOutOfRangeException("length");
232 }
233
234 int currentOffset = offset;
235 int currentLength = length;
236
237 while ( currentLength > 0 ) {
238 if ( available <= 0 ) {
239 Fill();
240 if (available <= 0) {
241 return 0;
242 }
243 }
244
245 int toCopy = Math.Min(currentLength, available);
246 Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy);
247 currentOffset += toCopy;
248 currentLength -= toCopy;
249 available -= toCopy;
250 }
251 return length;
252 }
253
254 /// <summary>
255 /// Read a <see cref="byte"/> from the input stream.
256 /// </summary>
257 /// <returns>Returns the byte read.</returns>
258 public int ReadLeByte()
259 {
260 if (available <= 0) {
261 Fill();
262 if (available <= 0) {
263 throw new ZipException("EOF in header");
264 }
265 }
266 byte result = rawData[rawLength - available];
267 available -= 1;
268 return result;
269 }
270
271 /// <summary>
272 /// Read an <see cref="short"/> in little endian byte order.
273 /// </summary>
274 /// <returns>The short value read case to an int.</returns>
275 public int ReadLeShort()
276 {
277 return ReadLeByte() | (ReadLeByte() << 8);
278 }
279
280 /// <summary>
281 /// Read an <see cref="int"/> in little endian byte order.
282 /// </summary>
283 /// <returns>The int value read.</returns>
284 public int ReadLeInt()
285 {
286 return ReadLeShort() | (ReadLeShort() << 16);
287 }
288
289 /// <summary>
290 /// Read a <see cref="long"/> in little endian byte order.
291 /// </summary>
292 /// <returns>The long value read.</returns>
293 public long ReadLeLong()
294 {
295 return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
296 }
297
298 #if !NETCF_1_0
299 /// <summary>
300 /// Get/set the <see cref="ICryptoTransform"/> to apply to any data.
301 /// </summary>
302 /// <remarks>Set this value to null to have no transform applied.</remarks>
303 public ICryptoTransform CryptoTransform
304 {
305 set {
306 cryptoTransform = value;
307 if ( cryptoTransform != null ) {
308 if ( rawData == clearText ) {
309 if ( internalClearText == null ) {
310 internalClearText = new byte[rawData.Length];
311 }
312 clearText = internalClearText;
313 }
314 clearTextLength = rawLength;
315 if ( available > 0 ) {
316 cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
317 }
318 } else {
319 clearText = rawData;
320 clearTextLength = rawLength;
321 }
322 }
323 }
324 #endif
325
326 #region Instance Fields
327 int rawLength;
328 byte[] rawData;
329
330 int clearTextLength;
331 byte[] clearText;
332 #if !NETCF_1_0
333 byte[] internalClearText;
334 #endif
335
336 int available;
337
338 #if !NETCF_1_0
339 ICryptoTransform cryptoTransform;
340 #endif
341 Stream inputStream;
342 #endregion
343 }
344
345 /// <summary>
346 /// This filter stream is used to decompress data compressed using the "deflate"
347 /// format. The "deflate" format is described in RFC 1951.
348 ///
349 /// This stream may form the basis for other decompression filters, such
350 /// as the <see cref="ICSharpCode.SharpZipLib.GZip.GZipInputStream">GZipInputStream</see>.
351 ///
352 /// Author of the original java version : John Leuner.
353 /// </summary>
354 public class InflaterInputStream : Stream
355 {
356 #region Constructors
357 /// <summary>
358 /// Create an InflaterInputStream with the default decompressor
359 /// and a default buffer size of 4KB.
360 /// </summary>
361 /// <param name = "baseInputStream">
362 /// The InputStream to read bytes from
363 /// </param>
364 public InflaterInputStream(Stream baseInputStream)
365 : this(baseInputStream, new Inflater(), 4096)
366 {
367 }
368
369 /// <summary>
370 /// Create an InflaterInputStream with the specified decompressor
371 /// and a default buffer size of 4KB.
372 /// </summary>
373 /// <param name = "baseInputStream">
374 /// The source of input data
375 /// </param>
376 /// <param name = "inf">
377 /// The decompressor used to decompress data read from baseInputStream
378 /// </param>
379 public InflaterInputStream(Stream baseInputStream, Inflater inf)
380 : this(baseInputStream, inf, 4096)
381 {
382 }
383
384 /// <summary>
385 /// Create an InflaterInputStream with the specified decompressor
386 /// and the specified buffer size.
387 /// </summary>
388 /// <param name = "baseInputStream">
389 /// The InputStream to read bytes from
390 /// </param>
391 /// <param name = "inflater">
392 /// The decompressor to use
393 /// </param>
394 /// <param name = "bufferSize">
395 /// Size of the buffer to use
396 /// </param>
397 public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize)
398 {
399 if (baseInputStream == null) {
400 throw new ArgumentNullException("baseInputStream");
401 }
402
403 if (inflater == null) {
404 throw new ArgumentNullException("inflater");
405 }
406
407 if (bufferSize <= 0) {
408 throw new ArgumentOutOfRangeException("bufferSize");
409 }
410
411 this.baseInputStream = baseInputStream;
412 this.inf = inflater;
413
414 inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize);
415 }
416
417 #endregion
418
419 /// <summary>
420 /// Get/set flag indicating ownership of underlying stream.
421 /// When the flag is true <see cref="Close"/> will close the underlying stream also.
422 /// </summary>
423 /// <remarks>
424 /// The default value is true.
425 /// </remarks>
426 public bool IsStreamOwner
427 {
428 get { return isStreamOwner; }
429 set { isStreamOwner = value; }
430 }
431
432 /// <summary>
433 /// Skip specified number of bytes of uncompressed data
434 /// </summary>
435 /// <param name ="count">
436 /// Number of bytes to skip
437 /// </param>
438 /// <returns>
439 /// The number of bytes skipped, zero if the end of
440 /// stream has been reached
441 /// </returns>
442 /// <exception cref="ArgumentOutOfRangeException">
443 /// <paramref name="count">The number of bytes</paramref> to skip is less than or equal to zero.
444 /// </exception>
445 public long Skip(long count)
446 {
447 if (count <= 0) {
448 throw new ArgumentOutOfRangeException("count");
449 }
450
451 // v0.80 Skip by seeking if underlying stream supports it...
452 if (baseInputStream.CanSeek) {
453 baseInputStream.Seek(count, SeekOrigin.Current);
454 return count;
455 }
456 else {
457 int length = 2048;
458 if (count < length) {
459 length = (int) count;
460 }
461
462 byte[] tmp = new byte[length];
463 int readCount = 1;
464 long toSkip = count;
465
466 while ((toSkip > 0) && (readCount > 0) ) {
467 if (toSkip < length) {
468 length = (int)toSkip;
469 }
470
471 readCount = baseInputStream.Read(tmp, 0, length);
472 toSkip -= readCount;
473 }
474
475 return count - toSkip;
476 }
477 }
478
479 /// <summary>
480 /// Clear any cryptographic state.
481 /// </summary>
482 protected void StopDecrypting()
483 {
484 #if !NETCF_1_0
485 inputBuffer.CryptoTransform = null;
486 #endif
487 }
488
489 /// <summary>
490 /// Returns 0 once the end of the stream (EOF) has been reached.
491 /// Otherwise returns 1.
492 /// </summary>
493 public virtual int Available
494 {
495 get {
496 return inf.IsFinished ? 0 : 1;
497 }
498 }
499
500 /// <summary>
501 /// Fills the buffer with more data to decompress.
502 /// </summary>
503 /// <exception cref="SharpZipBaseException">
504 /// Stream ends early
505 /// </exception>
506 protected void Fill()
507 {
508 // Protect against redundant calls
509 if (inputBuffer.Available <= 0) {
510 inputBuffer.Fill();
511 if (inputBuffer.Available <= 0) {
512 throw new SharpZipBaseException("Unexpected EOF");
513 }
514 }
515 inputBuffer.SetInflaterInput(inf);
516 }
517
518 #region Stream Overrides
519 /// <summary>
520 /// Gets a value indicating whether the current stream supports reading
521 /// </summary>
522 public override bool CanRead
523 {
524 get {
525 return baseInputStream.CanRead;
526 }
527 }
528
529 /// <summary>
530 /// Gets a value of false indicating seeking is not supported for this stream.
531 /// </summary>
532 public override bool CanSeek {
533 get {
534 return false;
535 }
536 }
537
538 /// <summary>
539 /// Gets a value of false indicating that this stream is not writeable.
540 /// </summary>
541 public override bool CanWrite {
542 get {
543 return false;
544 }
545 }
546
547 /// <summary>
548 /// A value representing the length of the stream in bytes.
549 /// </summary>
550 public override long Length {
551 get {
552 return inputBuffer.RawLength;
553 }
554 }
555
556 /// <summary>
557 /// The current position within the stream.
558 /// Throws a NotSupportedException when attempting to set the position
559 /// </summary>
560 /// <exception cref="NotSupportedException">Attempting to set the position</exception>
561 public override long Position {
562 get {
563 return baseInputStream.Position;
564 }
565 set {
566 throw new NotSupportedException("InflaterInputStream Position not supported");
567 }
568 }
569
570 /// <summary>
571 /// Flushes the baseInputStream
572 /// </summary>
573 public override void Flush()
574 {
575 baseInputStream.Flush();
576 }
577
578 /// <summary>
579 /// Sets the position within the current stream
580 /// Always throws a NotSupportedException
581 /// </summary>
582 /// <param name="offset">The relative offset to seek to.</param>
583 /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
584 /// <returns>The new position in the stream.</returns>
585 /// <exception cref="NotSupportedException">Any access</exception>
586 public override long Seek(long offset, SeekOrigin origin)
587 {
588 throw new NotSupportedException("Seek not supported");
589 }
590
591 /// <summary>
592 /// Set the length of the current stream
593 /// Always throws a NotSupportedException
594 /// </summary>
595 /// <param name="value">The new length value for the stream.</param>
596 /// <exception cref="NotSupportedException">Any access</exception>
597 public override void SetLength(long value)
598 {
599 throw new NotSupportedException("InflaterInputStream SetLength not supported");
600 }
601
602 /// <summary>
603 /// Writes a sequence of bytes to stream and advances the current position
604 /// This method always throws a NotSupportedException
605 /// </summary>
606 /// <param name="buffer">Thew buffer containing data to write.</param>
607 /// <param name="offset">The offset of the first byte to write.</param>
608 /// <param name="count">The number of bytes to write.</param>
609 /// <exception cref="NotSupportedException">Any access</exception>
610 public override void Write(byte[] buffer, int offset, int count)
611 {
612 throw new NotSupportedException("InflaterInputStream Write not supported");
613 }
614
615 /// <summary>
616 /// Writes one byte to the current stream and advances the current position
617 /// Always throws a NotSupportedException
618 /// </summary>
619 /// <param name="value">The byte to write.</param>
620 /// <exception cref="NotSupportedException">Any access</exception>
621 public override void WriteByte(byte value)
622 {
623 throw new NotSupportedException("InflaterInputStream WriteByte not supported");
624 }
625
626 /// <summary>
627 /// Entry point to begin an asynchronous write. Always throws a NotSupportedException.
628 /// </summary>
629 /// <param name="buffer">The buffer to write data from</param>
630 /// <param name="offset">Offset of first byte to write</param>
631 /// <param name="count">The maximum number of bytes to write</param>
632 /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
633 /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests</param>
634 /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
635 /// <exception cref="NotSupportedException">Any access</exception>
636 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
637 {
638 throw new NotSupportedException("InflaterInputStream BeginWrite not supported");
639 }
640
641 /// <summary>
642 /// Closes the input stream. When <see cref="IsStreamOwner"></see>
643 /// is true the underlying stream is also closed.
644 /// </summary>
645 public override void Close()
646 {
647 if ( !isClosed ) {
648 isClosed = true;
649 if ( isStreamOwner ) {
650 baseInputStream.Close();
651 }
652 }
653 }
654
655 /// <summary>
656 /// Reads decompressed data into the provided buffer byte array
657 /// </summary>
658 /// <param name ="buffer">
659 /// The array to read and decompress data into
660 /// </param>
661 /// <param name ="offset">
662 /// The offset indicating where the data should be placed
663 /// </param>
664 /// <param name ="count">
665 /// The number of bytes to decompress
666 /// </param>
667 /// <returns>The number of bytes read. Zero signals the end of stream</returns>
668 /// <exception cref="SharpZipBaseException">
669 /// Inflater needs a dictionary
670 /// </exception>
671 public override int Read(byte[] buffer, int offset, int count)
672 {
673 if (inf.IsNeedingDictionary)
674 {
675 throw new SharpZipBaseException("Need a dictionary");
676 }
677
678 int remainingBytes = count;
679 while (true) {
680 int bytesRead = inf.Inflate(buffer, offset, remainingBytes);
681 offset += bytesRead;
682 remainingBytes -= bytesRead;
683
684 if (remainingBytes == 0 || inf.IsFinished) {
685 break;
686 }
687
688 if ( inf.IsNeedingInput ) {
689 Fill();
690 }
691 else if ( bytesRead == 0 ) {
692 throw new ZipException("Dont know what to do");
693 }
694 }
695 return count - remainingBytes;
696 }
697 #endregion
698
699 #region Instance Fields
700 /// <summary>
701 /// Decompressor for this stream
702 /// </summary>
703 protected Inflater inf;
704
705 /// <summary>
706 /// <see cref="InflaterInputBuffer">Input buffer</see> for this stream.
707 /// </summary>
708 protected InflaterInputBuffer inputBuffer;
709
710 /// <summary>
711 /// Base stream the inflater reads from.
712 /// </summary>
713 private Stream baseInputStream;
714
715 /// <summary>
716 /// The compressed size
717 /// </summary>
718 protected long csize;
719
720 /// <summary>
721 /// Flag indicating wether this instance has been closed or not.
722 /// </summary>
723 bool isClosed;
724
725 /// <summary>
726 /// Flag indicating wether this instance is designated the stream owner.
727 /// When closing if this flag is true the underlying stream is closed.
728 /// </summary>
729 bool isStreamOwner = true;
730 #endregion
731 }
732 }