/** * Decodes the huffman encoded symbols in the input stream. * * @return false if more input is needed, true if output window is full or the current block ends. * @exception DataFormatException if deflated stream is invalid. */ private boolean decodeHuffman() throws DataFormatException { int free = outputWindow.getFreeSpace(); while (free >= 258) { int symbol; switch (mode) { case DECODE_HUFFMAN: /* This is the inner loop so it is optimized a bit */ while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0) { outputWindow.write(symbol); if (--free < 258) return true; } if (symbol < 257) { if (symbol < 0) return false; else { /* symbol == 256: end of block */ distTree = null; litlenTree = null; mode = DECODE_BLOCKS; return true; } } try { repLength = CPLENS[symbol - 257]; neededBits = CPLEXT[symbol - 257]; } catch (ArrayIndexOutOfBoundsException ex) { throw new DataFormatException("Illegal rep length code"); } /* fall through */ case DECODE_HUFFMAN_LENBITS: if (neededBits > 0) { mode = DECODE_HUFFMAN_LENBITS; int i = input.peekBits(neededBits); if (i < 0) return false; input.dropBits(neededBits); repLength += i; } mode = DECODE_HUFFMAN_DIST; /* fall through */ case DECODE_HUFFMAN_DIST: symbol = distTree.getSymbol(input); if (symbol < 0) return false; try { repDist = CPDIST[symbol]; neededBits = CPDEXT[symbol]; } catch (ArrayIndexOutOfBoundsException ex) { throw new DataFormatException("Illegal rep dist code"); } /* fall through */ case DECODE_HUFFMAN_DISTBITS: if (neededBits > 0) { mode = DECODE_HUFFMAN_DISTBITS; int i = input.peekBits(neededBits); if (i < 0) return false; input.dropBits(neededBits); repDist += i; } outputWindow.repeat(repLength, repDist); free -= repLength; mode = DECODE_HUFFMAN; break; default: throw new IllegalStateException(); } } return true; }
public boolean decode(final StreamManipulator input) throws DataFormatException { decode_loop: for (; ; ) { switch (mode) { case LNUM: lnum = input.peekBits(5); if (lnum < 0) { return false; } lnum += 257; input.dropBits(5); // System.err.println("LNUM: "+lnum); mode = DNUM; /* fall through */ case DNUM: dnum = input.peekBits(5); if (dnum < 0) { return false; } dnum++; input.dropBits(5); // System.err.println("DNUM: "+dnum); num = lnum + dnum; litdistLens = new byte[num]; mode = BLNUM; /* fall through */ case BLNUM: blnum = input.peekBits(4); if (blnum < 0) { return false; } blnum += 4; input.dropBits(4); blLens = new byte[19]; ptr = 0; // System.err.println("BLNUM: "+blnum); mode = BLLENS; /* fall through */ case BLLENS: while (ptr < blnum) { final int len = input.peekBits(3); if (len < 0) { return false; } input.dropBits(3); // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); blLens[BL_ORDER[ptr]] = (byte) len; ptr++; } blTree = new InflaterHuffmanTree(blLens); blLens = null; ptr = 0; mode = LENS; /* fall through */ case LENS: { int symbol; while (((symbol = blTree.getSymbol(input)) & ~15) == 0) { /* Normal case: symbol in [0..15] */ // System.err.println("litdistLens["+ptr+"]: "+symbol); litdistLens[ptr++] = lastLen = (byte) symbol; if (ptr == num) { /* Finished */ return true; } } /* need more input ? */ if (symbol < 0) { return false; } /* otherwise repeat code */ if (symbol >= 17) { /* repeat zero */ // System.err.println("repeating zero"); lastLen = 0; } else { if (ptr == 0) { throw new DataFormatException(); } } repSymbol = symbol - 16; mode = REPS; } /* fall through */ case REPS: { final int bits = repBits[repSymbol]; int count = input.peekBits(bits); if (count < 0) { return false; } input.dropBits(bits); count += repMin[repSymbol]; // System.err.println("litdistLens repeated: "+count); if ((ptr + count) > num) { throw new DataFormatException(); } while (count-- > 0) { litdistLens[ptr++] = lastLen; } if (ptr == num) { /* Finished */ return true; } } mode = LENS; continue decode_loop; } } }