Summary

Class:ICSharpCode.SharpZipLib.Lzw.LzwInputStream
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Lzw\LzwInputStream.cs
Covered lines:0
Uncovered lines:194
Coverable lines:194
Total lines:559
Line coverage:0%
Branch coverage:0%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor(...)100
ReadByte()200
Read(...)2500
ResetBuf(...)100
Fill()200
ParseHeader()900
Flush()100
Seek(...)100
SetLength(...)100
Write(...)100
WriteByte(...)100
BeginWrite(...)100
Close()300

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Lzw\LzwInputStream.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3
 4namespace ICSharpCode.SharpZipLib.Lzw
 5{
 6  /// <summary>
 7  /// This filter stream is used to decompress a LZW format stream.
 8  /// Specifically, a stream that uses the LZC compression method.
 9  /// This file format is usually associated with the .Z file extension.
 10  ///
 11  /// See http://en.wikipedia.org/wiki/Compress
 12  /// See http://wiki.wxwidgets.org/Development:_Z_File_Format
 13  ///
 14  /// The file header consists of 3 (or optionally 4) bytes. The first two bytes
 15  /// contain the magic marker "0x1f 0x9d", followed by a byte of flags.
 16  ///
 17  /// Based on Java code by Ronald Tschalar, which in turn was based on the unlzw.c
 18  /// code in the gzip package.
 19  /// </summary>
 20  /// <example> This sample shows how to unzip a compressed file
 21  /// <code>
 22  /// using System;
 23  /// using System.IO;
 24  ///
 25  /// using ICSharpCode.SharpZipLib.Core;
 26  /// using ICSharpCode.SharpZipLib.LZW;
 27  ///
 28  /// class MainClass
 29  /// {
 30  ///   public static void Main(string[] args)
 31  ///   {
 32  ///      using (Stream inStream = new LzwInputStream(File.OpenRead(args[0])))
 33  ///      using (FileStream outStream = File.Create(Path.GetFileNameWithoutExtension(args[0]))) {
 34  ///        byte[] buffer = new byte[4096];
 35  ///        StreamUtils.Copy(inStream, outStream, buffer);
 36  ///                         // OR
 37  ///                         inStream.Read(buffer, 0, buffer.Length);
 38  ///                         // now do something with the buffer
 39  ///     }
 40  ///   }
 41  /// }
 42  /// </code>
 43  /// </example>
 44  public class LzwInputStream : Stream
 45  {
 46    /// <summary>
 47    /// Get/set flag indicating ownership of underlying stream.
 48    /// When the flag is true <see cref="Close"/> will close the underlying stream also.
 49    /// </summary>
 50    /// <remarks>
 51    /// The default value is true.
 52    /// </remarks>
 53    public bool IsStreamOwner {
 054      get { return isStreamOwner; }
 055      set { isStreamOwner = value; }
 56    }
 57
 58    /// <summary>
 59    /// Creates a LzwInputStream
 60    /// </summary>
 61    /// <param name="baseInputStream">
 62    /// The stream to read compressed data from (baseInputStream LZW format)
 63    /// </param>
 064    public LzwInputStream(Stream baseInputStream)
 65    {
 066      this.baseInputStream = baseInputStream;
 067    }
 68
 69    /// <summary>
 70    /// See <see cref="System.IO.Stream.ReadByte"/>
 71    /// </summary>
 72    /// <returns></returns>
 73    public override int ReadByte()
 74    {
 075      int b = Read(one, 0, 1);
 076       if (b == 1)
 077        return (one[0] & 0xff);
 078      return -1;
 79    }
 80
 81    /// <summary>
 82    /// Reads decompressed data into the provided buffer byte array
 83    /// </summary>
 84    /// <param name ="buffer">
 85    /// The array to read and decompress data into
 86    /// </param>
 87    /// <param name ="offset">
 88    /// The offset indicating where the data should be placed
 89    /// </param>
 90    /// <param name ="count">
 91    /// The number of bytes to decompress
 92    /// </param>
 93    /// <returns>The number of bytes read. Zero signals the end of stream</returns>
 94    public override int Read(byte[] buffer, int offset, int count)
 95    {
 096       if (!headerParsed)
 097        ParseHeader();
 98
 099       if (eof)
 0100        return 0;
 101
 0102      int start = offset;
 103
 104      /* Using local copies of various variables speeds things up by as
 105           * much as 30% in Java! Performance not tested in C#.
 106           */
 0107      int[] lTabPrefix = tabPrefix;
 0108      byte[] lTabSuffix = tabSuffix;
 0109      byte[] lStack = stack;
 0110      int lNBits = nBits;
 0111      int lMaxCode = maxCode;
 0112      int lMaxMaxCode = maxMaxCode;
 0113      int lBitMask = bitMask;
 0114      int lOldCode = oldCode;
 0115      byte lFinChar = finChar;
 0116      int lStackP = stackP;
 0117      int lFreeEnt = freeEnt;
 0118      byte[] lData = data;
 0119      int lBitPos = bitPos;
 120
 121
 122      // empty stack if stuff still left
 0123      int sSize = lStack.Length - lStackP;
 0124       if (sSize > 0) {
 0125         int num = (sSize >= count) ? count : sSize;
 0126        Array.Copy(lStack, lStackP, buffer, offset, num);
 0127        offset += num;
 0128        count -= num;
 0129        lStackP += num;
 130      }
 131
 0132       if (count == 0) {
 0133        stackP = lStackP;
 0134        return offset - start;
 135      }
 136
 137
 138      // loop, filling local buffer until enough data has been decompressed
 139      MainLoop:
 140      do {
 0141         if (end < EXTRA) {
 0142          Fill();
 143        }
 144
 0145        int bitIn = (got > 0) ? (end - end % lNBits) << 3 :
 0146                    (end << 3) - (lNBits - 1);
 147
 0148         while (lBitPos < bitIn) {
 149          #region A
 150          // handle 1-byte reads correctly
 0151           if (count == 0) {
 0152            nBits = lNBits;
 0153            maxCode = lMaxCode;
 0154            maxMaxCode = lMaxMaxCode;
 0155            bitMask = lBitMask;
 0156            oldCode = lOldCode;
 0157            finChar = lFinChar;
 0158            stackP = lStackP;
 0159            freeEnt = lFreeEnt;
 0160            bitPos = lBitPos;
 161
 0162            return offset - start;
 163          }
 164
 165          // check for code-width expansion
 0166           if (lFreeEnt > lMaxCode) {
 0167            int nBytes = lNBits << 3;
 0168            lBitPos = (lBitPos - 1) +
 0169            nBytes - (lBitPos - 1 + nBytes) % nBytes;
 170
 0171            lNBits++;
 0172            lMaxCode = (lNBits == maxBits) ? lMaxMaxCode :
 0173                            (1 << lNBits) - 1;
 174
 0175            lBitMask = (1 << lNBits) - 1;
 0176            lBitPos = ResetBuf(lBitPos);
 0177            goto MainLoop;
 178          }
 179          #endregion
 180
 181          #region B
 182          // read next code
 0183          int pos = lBitPos >> 3;
 0184          int code = (((lData[pos] & 0xFF) |
 0185            ((lData[pos + 1] & 0xFF) << 8) |
 0186            ((lData[pos + 2] & 0xFF) << 16)) >>
 0187            (lBitPos & 0x7)) & lBitMask;
 188
 0189          lBitPos += lNBits;
 190
 191          // handle first iteration
 0192           if (lOldCode == -1) {
 0193             if (code >= 256)
 0194              throw new LzwException("corrupt input: " + code + " > 255");
 195
 0196            lFinChar = (byte)(lOldCode = code);
 0197            buffer[offset++] = lFinChar;
 0198            count--;
 0199            continue;
 200          }
 201
 202          // handle CLEAR code
 0203           if (code == TBL_CLEAR && blockMode) {
 0204            Array.Copy(zeros, 0, lTabPrefix, 0, zeros.Length);
 0205            lFreeEnt = TBL_FIRST - 1;
 206
 0207            int nBytes = lNBits << 3;
 0208            lBitPos = (lBitPos - 1) + nBytes - (lBitPos - 1 + nBytes) % nBytes;
 0209            lNBits = LzwConstants.INIT_BITS;
 0210            lMaxCode = (1 << lNBits) - 1;
 0211            lBitMask = lMaxCode;
 212
 213            // Code tables reset
 214
 0215            lBitPos = ResetBuf(lBitPos);
 0216            goto MainLoop;
 217          }
 218          #endregion
 219
 220          #region C
 221          // setup
 0222          int inCode = code;
 0223          lStackP = lStack.Length;
 224
 225          // Handle KwK case
 0226           if (code >= lFreeEnt) {
 0227             if (code > lFreeEnt) {
 0228              throw new LzwException("corrupt input: code=" + code +
 0229                ", freeEnt=" + lFreeEnt);
 230            }
 231
 0232            lStack[--lStackP] = lFinChar;
 0233            code = lOldCode;
 234          }
 235
 236          // Generate output characters in reverse order
 0237           while (code >= 256) {
 0238            lStack[--lStackP] = lTabSuffix[code];
 0239            code = lTabPrefix[code];
 240          }
 241
 0242          lFinChar = lTabSuffix[code];
 0243          buffer[offset++] = lFinChar;
 0244          count--;
 245
 246          // And put them out in forward order
 0247          sSize = lStack.Length - lStackP;
 0248           int num = (sSize >= count) ? count : sSize;
 0249          Array.Copy(lStack, lStackP, buffer, offset, num);
 0250          offset += num;
 0251          count -= num;
 0252          lStackP += num;
 253          #endregion
 254
 255          #region D
 256          // generate new entry in table
 0257           if (lFreeEnt < lMaxMaxCode) {
 0258            lTabPrefix[lFreeEnt] = lOldCode;
 0259            lTabSuffix[lFreeEnt] = lFinChar;
 0260            lFreeEnt++;
 261          }
 262
 263          // Remember previous code
 0264          lOldCode = inCode;
 265
 266          // if output buffer full, then return
 0267           if (count == 0) {
 0268            nBits = lNBits;
 0269            maxCode = lMaxCode;
 0270            bitMask = lBitMask;
 0271            oldCode = lOldCode;
 0272            finChar = lFinChar;
 0273            stackP = lStackP;
 0274            freeEnt = lFreeEnt;
 0275            bitPos = lBitPos;
 276
 0277            return offset - start;
 278          }
 279          #endregion
 280        }   // while
 281
 0282        lBitPos = ResetBuf(lBitPos);
 283
 0284       } while (got > 0);  // do..while
 285
 0286      nBits = lNBits;
 0287      maxCode = lMaxCode;
 0288      bitMask = lBitMask;
 0289      oldCode = lOldCode;
 0290      finChar = lFinChar;
 0291      stackP = lStackP;
 0292      freeEnt = lFreeEnt;
 0293      bitPos = lBitPos;
 294
 0295      eof = true;
 0296      return offset - start;
 297    }
 298
 299    /// <summary>
 300    /// Moves the unread data in the buffer to the beginning and resets
 301    /// the pointers.
 302    /// </summary>
 303    /// <param name="bitPosition"></param>
 304    /// <returns></returns>
 305    private int ResetBuf(int bitPosition)
 306    {
 0307      int pos = bitPosition >> 3;
 0308      Array.Copy(data, pos, data, 0, end - pos);
 0309      end -= pos;
 0310      return 0;
 311    }
 312
 313
 314    private void Fill()
 315    {
 0316      got = baseInputStream.Read(data, end, data.Length - 1 - end);
 0317       if (got > 0) {
 0318        end += got;
 319      }
 0320    }
 321
 322
 323    private void ParseHeader()
 324    {
 0325      headerParsed = true;
 326
 0327      byte[] hdr = new byte[LzwConstants.HDR_SIZE];
 328
 0329      int result = baseInputStream.Read(hdr, 0, hdr.Length);
 330
 331      // Check the magic marker
 0332       if (result < 0)
 0333        throw new LzwException("Failed to read LZW header");
 334
 0335       if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff)) {
 0336        throw new LzwException(String.Format(
 0337          "Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
 0338          hdr[0], hdr[1]));
 339      }
 340
 341      // Check the 3rd header byte
 0342      blockMode = (hdr[2] & LzwConstants.BLOCK_MODE_MASK) > 0;
 0343      maxBits = hdr[2] & LzwConstants.BIT_MASK;
 344
 0345       if (maxBits > LzwConstants.MAX_BITS) {
 0346        throw new LzwException("Stream compressed with " + maxBits +
 0347          " bits, but decompression can only handle " +
 0348          LzwConstants.MAX_BITS + " bits.");
 349      }
 350
 0351       if ((hdr[2] & LzwConstants.RESERVED_MASK) > 0) {
 0352        throw new LzwException("Unsupported bits set in the header.");
 353      }
 354
 355      // Initialize variables
 0356      maxMaxCode = 1 << maxBits;
 0357      nBits = LzwConstants.INIT_BITS;
 0358      maxCode = (1 << nBits) - 1;
 0359      bitMask = maxCode;
 0360      oldCode = -1;
 0361      finChar = 0;
 0362       freeEnt = blockMode ? TBL_FIRST : 256;
 363
 0364      tabPrefix = new int[1 << maxBits];
 0365      tabSuffix = new byte[1 << maxBits];
 0366      stack = new byte[1 << maxBits];
 0367      stackP = stack.Length;
 368
 0369       for (int idx = 255; idx >= 0; idx--)
 0370        tabSuffix[idx] = (byte)idx;
 0371    }
 372
 373    #region Stream Overrides
 374    /// <summary>
 375    /// Gets a value indicating whether the current stream supports reading
 376    /// </summary>
 377    public override bool CanRead {
 378      get {
 0379        return baseInputStream.CanRead;
 380      }
 381    }
 382
 383    /// <summary>
 384    /// Gets a value of false indicating seeking is not supported for this stream.
 385    /// </summary>
 386    public override bool CanSeek {
 387      get {
 0388        return false;
 389      }
 390    }
 391
 392    /// <summary>
 393    /// Gets a value of false indicating that this stream is not writeable.
 394    /// </summary>
 395    public override bool CanWrite {
 396      get {
 0397        return false;
 398      }
 399    }
 400
 401    /// <summary>
 402    /// A value representing the length of the stream in bytes.
 403    /// </summary>
 404    public override long Length {
 405      get {
 0406        return got;
 407      }
 408    }
 409
 410    /// <summary>
 411    /// The current position within the stream.
 412    /// Throws a NotSupportedException when attempting to set the position
 413    /// </summary>
 414    /// <exception cref="NotSupportedException">Attempting to set the position</exception>
 415    public override long Position {
 416      get {
 0417        return baseInputStream.Position;
 418      }
 419      set {
 0420        throw new NotSupportedException("InflaterInputStream Position not supported");
 421      }
 422    }
 423
 424    /// <summary>
 425    /// Flushes the baseInputStream
 426    /// </summary>
 427    public override void Flush()
 428    {
 0429      baseInputStream.Flush();
 0430    }
 431
 432    /// <summary>
 433    /// Sets the position within the current stream
 434    /// Always throws a NotSupportedException
 435    /// </summary>
 436    /// <param name="offset">The relative offset to seek to.</param>
 437    /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
 438    /// <returns>The new position in the stream.</returns>
 439    /// <exception cref="NotSupportedException">Any access</exception>
 440    public override long Seek(long offset, SeekOrigin origin)
 441    {
 0442      throw new NotSupportedException("Seek not supported");
 443    }
 444
 445    /// <summary>
 446    /// Set the length of the current stream
 447    /// Always throws a NotSupportedException
 448    /// </summary>
 449    /// <param name="value">The new length value for the stream.</param>
 450    /// <exception cref="NotSupportedException">Any access</exception>
 451    public override void SetLength(long value)
 452    {
 0453      throw new NotSupportedException("InflaterInputStream SetLength not supported");
 454    }
 455
 456    /// <summary>
 457    /// Writes a sequence of bytes to stream and advances the current position
 458    /// This method always throws a NotSupportedException
 459    /// </summary>
 460    /// <param name="buffer">Thew buffer containing data to write.</param>
 461    /// <param name="offset">The offset of the first byte to write.</param>
 462    /// <param name="count">The number of bytes to write.</param>
 463    /// <exception cref="NotSupportedException">Any access</exception>
 464    public override void Write(byte[] buffer, int offset, int count)
 465    {
 0466      throw new NotSupportedException("InflaterInputStream Write not supported");
 467    }
 468
 469    /// <summary>
 470    /// Writes one byte to the current stream and advances the current position
 471    /// Always throws a NotSupportedException
 472    /// </summary>
 473    /// <param name="value">The byte to write.</param>
 474    /// <exception cref="NotSupportedException">Any access</exception>
 475    public override void WriteByte(byte value)
 476    {
 0477      throw new NotSupportedException("InflaterInputStream WriteByte not supported");
 478    }
 479
 480    /// <summary>
 481    /// Entry point to begin an asynchronous write.  Always throws a NotSupportedException.
 482    /// </summary>
 483    /// <param name="buffer">The buffer to write data from</param>
 484    /// <param name="offset">Offset of first byte to write</param>
 485    /// <param name="count">The maximum number of bytes to write</param>
 486    /// <param name="callback">The method to be called when the asynchronous write operation is completed</param>
 487    /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from ot
 488    /// <returns>An <see cref="System.IAsyncResult">IAsyncResult</see> that references the asynchronous write</returns>
 489    /// <exception cref="NotSupportedException">Any access</exception>
 490    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
 491    {
 0492      throw new NotSupportedException("InflaterInputStream BeginWrite not supported");
 493    }
 494
 495    /// <summary>
 496    /// Closes the input stream.  When <see cref="IsStreamOwner"></see>
 497    /// is true the underlying stream is also closed.
 498    /// </summary>
 499    public override void Close()
 500    {
 0501       if (!isClosed) {
 0502        isClosed = true;
 0503         if (isStreamOwner) {
 0504          baseInputStream.Close();
 505        }
 506      }
 0507    }
 508
 509    #endregion
 510
 511    #region Instance Fields
 512
 513    Stream baseInputStream;
 514
 515    /// <summary>
 516    /// Flag indicating wether this instance is designated the stream owner.
 517    /// When closing if this flag is true the underlying stream is closed.
 518    /// </summary>
 0519    bool isStreamOwner = true;
 520
 521    /// <summary>
 522    /// Flag indicating wether this instance has been closed or not.
 523    /// </summary>
 524    bool isClosed;
 525
 0526    readonly byte[] one = new byte[1];
 527    bool headerParsed;
 528
 529    // string table stuff
 530    private const int TBL_CLEAR = 0x100;
 531    private const int TBL_FIRST = TBL_CLEAR + 1;
 532
 533    private int[] tabPrefix;
 534    private byte[] tabSuffix;
 0535    private readonly int[] zeros = new int[256];
 536    private byte[] stack;
 537
 538    // various state
 539    private bool blockMode;
 540    private int nBits;
 541    private int maxBits;
 542    private int maxMaxCode;
 543    private int maxCode;
 544    private int bitMask;
 545    private int oldCode;
 546    private byte finChar;
 547    private int stackP;
 548    private int freeEnt;
 549
 550    // input buffer
 0551    private readonly byte[] data = new byte[1024 * 8];
 552    private int bitPos;
 553    private int end;
 554    int got;
 555    private bool eof;
 556    private const int EXTRA = 64;
 557    #endregion
 558  }
 559}