Save This Page
Home » openjdk-7 » java » util » zip » [javadoc | source]
    1   /* DeflaterEngine.java --
    2      Copyright (C) 2001, 2004  Free Software Foundation, Inc.
    3   
    4   This file is part of GNU Classpath.
    5   
    6   GNU Classpath is free software; you can redistribute it and/or modify
    7   it under the terms of the GNU General Public License as published by
    8   the Free Software Foundation; either version 2, or (at your option)
    9   any later version.
   10   
   11   GNU Classpath is distributed in the hope that it will be useful, but
   12   WITHOUT ANY WARRANTY; without even the implied warranty of
   13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14   General Public License for more details.
   15   
   16   You should have received a copy of the GNU General Public License
   17   along with GNU Classpath; see the file COPYING.  If not, write to the
   18   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   19   02110-1301 USA.
   20   
   21   Linking this library statically or dynamically with other modules is
   22   making a combined work based on this library.  Thus, the terms and
   23   conditions of the GNU General Public License cover the whole
   24   combination.
   25   
   26   As a special exception, the copyright holders of this library give you
   27   permission to link this library with independent modules to produce an
   28   executable, regardless of the license terms of these independent
   29   modules, and to copy and distribute the resulting executable under
   30   terms of your choice, provided that you also meet, for each linked
   31   independent module, the terms and conditions of the license of that
   32   module.  An independent module is a module which is not derived from
   33   or based on this library.  If you modify this library, you may extend
   34   this exception to your version of the library, but you are not
   35   obligated to do so.  If you do not wish to do so, delete this
   36   exception statement from your version. */
   37   
   38   package java.util.zip;
   39   
   40   class DeflaterEngine implements DeflaterConstants
   41   {
   42     private static final int TOO_FAR = 4096;
   43   
   44     private int ins_h;
   45   
   46     /**
   47      * Hashtable, hashing three characters to an index for window, so
   48      * that window[index]..window[index+2] have this hash code.  
   49      * Note that the array should really be unsigned short, so you need
   50      * to and the values with 0xffff.
   51      */
   52     private short[] head;
   53   
   54     /**
   55      * prev[index & WMASK] points to the previous index that has the
   56      * same hash code as the string starting at index.  This way 
   57      * entries with the same hash code are in a linked list.
   58      * Note that the array should really be unsigned short, so you need
   59      * to and the values with 0xffff.
   60      */
   61     private short[] prev;
   62   
   63     private int matchStart, matchLen;
   64     private boolean prevAvailable;
   65     private int blockStart;
   66   
   67     /**
   68      * strstart points to the current character in window.
   69      */
   70     private int strstart;
   71   
   72     /**
   73      * lookahead is the number of characters starting at strstart in
   74      * window that are valid.
   75      * So window[strstart] until window[strstart+lookahead-1] are valid
   76      * characters.
   77      */
   78     private int lookahead;
   79   
   80     /**
   81      * This array contains the part of the uncompressed stream that 
   82      * is of relevance.  The current character is indexed by strstart.
   83      */
   84     private byte[] window;
   85   
   86     private int strategy, max_chain, max_lazy, niceLength, goodLength;
   87   
   88     /** The current compression function. */
   89     private int comprFunc;
   90   
   91     /** The input data for compression. */
   92     private byte[] inputBuf;
   93   
   94     /** The total bytes of input read. */
   95     private int totalIn;
   96   
   97     /** The offset into inputBuf, where input data starts. */
   98     private int inputOff;
   99   
  100     /** The end offset of the input data. */
  101     private int inputEnd;
  102   
  103     private DeflaterPending pending;
  104     private DeflaterHuffman huffman;
  105   
  106     /** The adler checksum */
  107     private Adler32 adler;
  108   
  109     /* DEFLATE ALGORITHM:
  110      *
  111      * The uncompressed stream is inserted into the window array.  When
  112      * the window array is full the first half is thrown away and the
  113      * second half is copied to the beginning.
  114      *
  115      * The head array is a hash table.  Three characters build a hash value
  116      * and they the value points to the corresponding index in window of 
  117      * the last string with this hash.  The prev array implements a
  118      * linked list of matches with the same hash: prev[index & WMASK] points
  119      * to the previous index with the same hash.
  120      * 
  121      * 
  122      */
  123   
  124   
  125     DeflaterEngine(DeflaterPending pending) {
  126       this.pending = pending;
  127       huffman = new DeflaterHuffman(pending);
  128       adler = new Adler32();
  129   
  130       window = new byte[2*WSIZE];
  131       head   = new short[HASH_SIZE];
  132       prev   = new short[WSIZE];
  133   
  134       /* We start at index 1, to avoid a implementation deficiency, that
  135        * we cannot build a repeat pattern at index 0.
  136        */
  137       blockStart = strstart = 1;
  138     }
  139   
  140     public void reset()
  141     {
  142       huffman.reset();
  143       adler.reset();
  144       blockStart = strstart = 1;
  145       lookahead = 0;
  146       totalIn = 0;
  147       prevAvailable = false;
  148       matchLen = MIN_MATCH - 1;
  149       for (int i = 0; i < HASH_SIZE; i++)
  150         head[i] = 0;
  151       for (int i = 0; i < WSIZE; i++)
  152         prev[i] = 0;
  153     }
  154   
  155     public final void resetAdler()
  156     {
  157       adler.reset();
  158     }
  159   
  160     public final int getAdler()
  161     {
  162       int chksum = (int) adler.getValue();
  163       return chksum;
  164     }
  165   
  166     public final int getTotalIn()
  167     {
  168       return totalIn;
  169     }
  170   
  171     public final void setStrategy(int strat)
  172     {
  173       strategy = strat;
  174     }
  175   
  176     public void setLevel(int lvl)
  177     {
  178       goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
  179       max_lazy    = DeflaterConstants.MAX_LAZY[lvl];
  180       niceLength = DeflaterConstants.NICE_LENGTH[lvl];
  181       max_chain   = DeflaterConstants.MAX_CHAIN[lvl];
  182   
  183       if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc) 
  184         {
  185   	if (DeflaterConstants.DEBUGGING)
  186   	  System.err.println("Change from "+comprFunc +" to "
  187   			     + DeflaterConstants.COMPR_FUNC[lvl]);
  188   	switch (comprFunc)
  189   	  {
  190   	  case DEFLATE_STORED:
  191   	    if (strstart > blockStart)
  192   	      {
  193   		huffman.flushStoredBlock(window, blockStart, 
  194   					 strstart - blockStart, false);
  195   		blockStart = strstart;
  196   	      }
  197   	    updateHash();
  198   	    break;
  199   	  case DEFLATE_FAST:
  200   	    if (strstart > blockStart)
  201   	      {
  202   		huffman.flushBlock(window, blockStart, strstart - blockStart,
  203   				   false);
  204   		blockStart = strstart;
  205   	      }
  206   	    break;
  207   	  case DEFLATE_SLOW:
  208   	    if (prevAvailable)
  209   	      huffman.tallyLit(window[strstart-1] & 0xff);
  210   	    if (strstart > blockStart)
  211   	      {
  212   		huffman.flushBlock(window, blockStart, strstart - blockStart,
  213   				   false);
  214   		blockStart = strstart;
  215   	      }
  216   	    prevAvailable = false;
  217   	    matchLen = MIN_MATCH - 1;
  218   	    break;
  219   	  }
  220   	comprFunc = COMPR_FUNC[lvl];
  221         }
  222     }
  223   
  224     private void updateHash() {
  225       if (DEBUGGING)
  226         System.err.println("updateHash: "+strstart);
  227       ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
  228     }    
  229   
  230     /**
  231      * Inserts the current string in the head hash and returns the previous
  232      * value for this hash.
  233      */
  234     private int insertString() {
  235       short match;
  236       int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)])
  237         & HASH_MASK;
  238   
  239       if (DEBUGGING)
  240         {
  241   	if (hash != (((window[strstart] << (2*HASH_SHIFT))
  242   		      ^ (window[strstart + 1] << HASH_SHIFT)
  243   		      ^ (window[strstart + 2])) & HASH_MASK))
  244   	  throw new InternalError("hash inconsistent: "+hash+"/"
  245   				  +window[strstart]+","
  246   				  +window[strstart+1]+","
  247   				  +window[strstart+2]+","+HASH_SHIFT);
  248         }
  249   
  250       prev[strstart & WMASK] = match = head[hash];
  251       head[hash] = (short) strstart;
  252       ins_h = hash;
  253       return match & 0xffff;
  254     }
  255   
  256     private void slideWindow()
  257     {
  258       System.arraycopy(window, WSIZE, window, 0, WSIZE);
  259       matchStart -= WSIZE;
  260       strstart -= WSIZE;
  261       blockStart -= WSIZE;
  262       
  263       /* Slide the hash table (could be avoided with 32 bit values
  264        * at the expense of memory usage).
  265        */
  266       for (int i = 0; i < HASH_SIZE; i++) 
  267         {
  268   	int m = head[i] & 0xffff;
  269   	head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
  270         }
  271   
  272       /* Slide the prev table.
  273        */
  274       for (int i = 0; i < WSIZE; i++) 
  275         {
  276   	int m = prev[i] & 0xffff;
  277   	prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
  278         }
  279     }
  280   
  281     /**
  282      * Fill the window when the lookahead becomes insufficient.
  283      * Updates strstart and lookahead.
  284      *
  285      * OUT assertions: strstart + lookahead <= 2*WSIZE
  286      *    lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd
  287      */
  288     private void fillWindow()
  289     {
  290       /* If the window is almost full and there is insufficient lookahead,
  291        * move the upper half to the lower one to make room in the upper half.
  292        */
  293       if (strstart >= WSIZE + MAX_DIST)
  294         slideWindow();
  295   
  296       /* If there is not enough lookahead, but still some input left,
  297        * read in the input
  298        */
  299       while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
  300         {
  301   	int more = 2*WSIZE - lookahead - strstart;
  302   	
  303   	if (more > inputEnd - inputOff)
  304   	  more = inputEnd - inputOff;
  305   
  306   	System.arraycopy(inputBuf, inputOff, 
  307   			 window, strstart + lookahead, more);
  308   	adler.update(inputBuf, inputOff, more);
  309   	inputOff += more;
  310   	totalIn  += more;
  311   	lookahead += more;
  312         }
  313   
  314       if (lookahead >= MIN_MATCH) 
  315         updateHash();
  316     }
  317   
  318     /**
  319      * Find the best (longest) string in the window matching the 
  320      * string starting at strstart.
  321      *
  322      * Preconditions:
  323      *    strstart + MAX_MATCH <= window.length.
  324      *    
  325      *
  326      * @param curMatch
  327      */
  328     private boolean findLongestMatch(int curMatch) {
  329       int chainLength = this.max_chain;
  330       int niceLength = this.niceLength;
  331       short[] prev = this.prev;
  332       int scan  = this.strstart;
  333       int match;
  334       int best_end = this.strstart + matchLen;
  335       int best_len = Math.max(matchLen, MIN_MATCH - 1);
  336       
  337       int limit = Math.max(strstart - MAX_DIST, 0);
  338   
  339       int strend = scan + MAX_MATCH - 1;
  340       byte scan_end1 = window[best_end - 1];
  341       byte scan_end  = window[best_end];
  342   
  343       /* Do not waste too much time if we already have a good match: */
  344       if (best_len >= this.goodLength)
  345         chainLength >>= 2;
  346   
  347       /* Do not look for matches beyond the end of the input. This is necessary
  348        * to make deflate deterministic.
  349        */
  350       if (niceLength > lookahead)
  351         niceLength = lookahead;
  352   
  353       if (DeflaterConstants.DEBUGGING 
  354   	&& strstart > 2*WSIZE - MIN_LOOKAHEAD)
  355         throw new InternalError("need lookahead");
  356       
  357       do {
  358         if (DeflaterConstants.DEBUGGING && curMatch >= strstart)
  359   	throw new InternalError("future match");
  360         if (window[curMatch + best_len] != scan_end
  361   	  || window[curMatch + best_len - 1] != scan_end1
  362   	  || window[curMatch] != window[scan]
  363   	  || window[curMatch+1] != window[scan + 1])
  364   	continue;
  365   
  366         match = curMatch + 2;      
  367         scan += 2;
  368   
  369         /* We check for insufficient lookahead only every 8th comparison;
  370          * the 256th check will be made at strstart+258.
  371          */
  372         while (window[++scan] == window[++match]
  373   	     && window[++scan] == window[++match]
  374   	     && window[++scan] == window[++match]
  375   	     && window[++scan] == window[++match]
  376   	     && window[++scan] == window[++match]
  377   	     && window[++scan] == window[++match]
  378   	     && window[++scan] == window[++match]
  379   	     && window[++scan] == window[++match]
  380   	     && scan < strend);
  381   
  382         if (scan > best_end) {
  383   //  	if (DeflaterConstants.DEBUGGING && ins_h == 0)
  384   //  	  System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
  385   	matchStart = curMatch;
  386   	best_end = scan;
  387   	best_len = scan - strstart;
  388   	if (best_len >= niceLength)
  389   	  break;
  390   
  391   	scan_end1  = window[best_end-1];
  392   	scan_end   = window[best_end];
  393         }
  394         scan = strstart;
  395       } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit
  396   	     && --chainLength != 0);
  397   
  398       matchLen = Math.min(best_len, lookahead);
  399       return matchLen >= MIN_MATCH;
  400     }
  401   
  402     void setDictionary(byte[] buffer, int offset, int length) {
  403       if (DeflaterConstants.DEBUGGING && strstart != 1)
  404         throw new IllegalStateException("strstart not 1");
  405       adler.update(buffer, offset, length);
  406       if (length < MIN_MATCH)
  407         return;
  408       if (length > MAX_DIST) {
  409         offset += length - MAX_DIST;
  410         length = MAX_DIST;
  411       }
  412   
  413       System.arraycopy(buffer, offset, window, strstart, length);
  414   
  415       updateHash();
  416       length--;
  417       while (--length > 0)
  418         {
  419   	insertString();
  420   	strstart++;
  421         }
  422       strstart += 2;
  423       blockStart = strstart;
  424     }    
  425       
  426     private boolean deflateStored(boolean flush, boolean finish)
  427     {
  428       if (!flush && lookahead == 0)
  429         return false;
  430   
  431       strstart += lookahead;
  432       lookahead = 0;
  433   
  434       int storedLen = strstart - blockStart;
  435   
  436       if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE) 
  437   	/* Block is full */
  438   	|| (blockStart < WSIZE && storedLen >= MAX_DIST)
  439   	/* Block may move out of window */
  440   	|| flush)
  441         {
  442   	boolean lastBlock = finish;
  443   	if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE)
  444   	  {
  445   	    storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
  446   	    lastBlock = false;
  447   	  }
  448   
  449   	if (DeflaterConstants.DEBUGGING)
  450   	  System.err.println("storedBlock["+storedLen+","+lastBlock+"]");
  451   
  452   	huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock);
  453   	blockStart += storedLen;
  454   	return !lastBlock;
  455         }
  456       return true;
  457     }
  458   
  459     private boolean deflateFast(boolean flush, boolean finish)
  460     {
  461       if (lookahead < MIN_LOOKAHEAD && !flush)
  462         return false;
  463   
  464       while (lookahead >= MIN_LOOKAHEAD || flush)
  465         {
  466   	if (lookahead == 0)
  467   	  {
  468   	    /* We are flushing everything */
  469   	    huffman.flushBlock(window, blockStart, strstart - blockStart,
  470   			       finish);
  471   	    blockStart = strstart;
  472   	    return false;
  473   	  }
  474   
  475   	if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
  476   	  {
  477   	    /* slide window, as findLongestMatch need this.
  478   	     * This should only happen when flushing and the window
  479   	     * is almost full.
  480   	     */
  481   	    slideWindow();
  482   	  }
  483   
  484   	int hashHead;
  485   	if (lookahead >= MIN_MATCH 
  486   	    && (hashHead = insertString()) != 0
  487   	    && strategy != Deflater.HUFFMAN_ONLY
  488   	    && strstart - hashHead <= MAX_DIST
  489   	    && findLongestMatch(hashHead))
  490   	  {
  491   	    /* longestMatch sets matchStart and matchLen */
  492   	    if (DeflaterConstants.DEBUGGING)
  493   	      {
  494   		for (int i = 0 ; i < matchLen; i++)
  495   		  {
  496   		    if (window[strstart+i] != window[matchStart + i])
  497   		      throw new InternalError();
  498   		  }
  499   	      }
  500   	    boolean full = huffman.tallyDist(strstart - matchStart, matchLen);
  501   	    
  502   	    lookahead -= matchLen;
  503   	    if (matchLen <= max_lazy && lookahead >= MIN_MATCH)
  504   	      {
  505   		while (--matchLen > 0)
  506   		  {
  507   		    strstart++;
  508   		    insertString();
  509   		  }
  510   		strstart++;
  511   	      }
  512   	    else
  513   	      {
  514   		strstart += matchLen;
  515   		if (lookahead >= MIN_MATCH - 1)
  516   		  updateHash();
  517   	      }
  518   	    matchLen = MIN_MATCH - 1;
  519   	    if (!full)
  520   	      continue;
  521   	  }
  522   	else
  523   	  {
  524   	    /* No match found */
  525   	    huffman.tallyLit(window[strstart] & 0xff);
  526   	    strstart++;
  527   	    lookahead--;
  528   	  }
  529   
  530   	if (huffman.isFull())
  531   	  {
  532   	    boolean lastBlock = finish && lookahead == 0;
  533   	    huffman.flushBlock(window, blockStart, strstart - blockStart,
  534   			       lastBlock);
  535   	    blockStart = strstart;
  536   	    return !lastBlock;
  537   	  }
  538         }
  539       return true;
  540     }
  541   
  542     private boolean deflateSlow(boolean flush, boolean finish)
  543     {
  544       if (lookahead < MIN_LOOKAHEAD && !flush)
  545         return false;
  546   
  547       while (lookahead >= MIN_LOOKAHEAD || flush)
  548         {
  549   	if (lookahead == 0)
  550   	  {
  551   	    if (prevAvailable)
  552   	      huffman.tallyLit(window[strstart-1] & 0xff);
  553   	    prevAvailable = false;
  554   
  555   	    /* We are flushing everything */
  556   	    if (DeflaterConstants.DEBUGGING && !flush)
  557   	      throw new InternalError("Not flushing, but no lookahead");
  558   	    huffman.flushBlock(window, blockStart, strstart - blockStart,
  559   			       finish);
  560   	    blockStart = strstart;
  561   	    return false;
  562   	  }
  563   
  564   	if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
  565   	  {
  566   	    /* slide window, as findLongestMatch need this.
  567   	     * This should only happen when flushing and the window
  568   	     * is almost full.
  569   	     */
  570   	    slideWindow();
  571   	  }
  572   
  573   	int prevMatch = matchStart;
  574   	int prevLen = matchLen;
  575   	if (lookahead >= MIN_MATCH)
  576   	  {
  577   	    int hashHead = insertString();
  578   	    if (strategy != Deflater.HUFFMAN_ONLY
  579   		&& hashHead != 0 && strstart - hashHead <= MAX_DIST
  580   		&& findLongestMatch(hashHead))
  581   	      {
  582   		/* longestMatch sets matchStart and matchLen */
  583   		
  584   		/* Discard match if too small and too far away */
  585   		if (matchLen <= 5
  586   		    && (strategy == Deflater.FILTERED
  587   			|| (matchLen == MIN_MATCH
  588   			    && strstart - matchStart > TOO_FAR))) {
  589   		  matchLen = MIN_MATCH - 1;
  590   		}
  591   	      }
  592   	  }
  593   	
  594   	/* previous match was better */
  595   	if (prevLen >= MIN_MATCH && matchLen <= prevLen)
  596   	  {
  597   	    if (DeflaterConstants.DEBUGGING)
  598   	      {
  599   		for (int i = 0 ; i < matchLen; i++)
  600   		  {
  601   		    if (window[strstart-1+i] != window[prevMatch + i])
  602   		      throw new InternalError();
  603   		  }
  604   	      }
  605   	    huffman.tallyDist(strstart - 1 - prevMatch, prevLen);
  606   	    prevLen -= 2;
  607   	    do 
  608   	      {
  609   		strstart++;
  610   		lookahead--;
  611   		if (lookahead >= MIN_MATCH)
  612   		  insertString();
  613   	      }
  614   	    while (--prevLen > 0);
  615   	    strstart ++;
  616   	    lookahead--;
  617   	    prevAvailable = false;
  618   	    matchLen = MIN_MATCH - 1;
  619   	  }
  620   	else
  621   	  {
  622   	    if (prevAvailable)
  623   	      huffman.tallyLit(window[strstart-1] & 0xff);
  624   	    prevAvailable = true;
  625   	    strstart++;
  626   	    lookahead--;
  627   	  }
  628   
  629   	if (huffman.isFull())
  630   	  {
  631   	    int len = strstart - blockStart;
  632   	    if (prevAvailable)
  633   	      len--;
  634   	    boolean lastBlock = (finish && lookahead == 0 && !prevAvailable);
  635   	    huffman.flushBlock(window, blockStart, len, lastBlock);
  636   	    blockStart += len;
  637   	    return !lastBlock;
  638   	  }
  639         }
  640       return true;
  641     } 
  642   
  643     public boolean deflate(boolean flush, boolean finish) 
  644     {
  645       boolean progress;
  646       do
  647         {
  648   	fillWindow();
  649   	boolean canFlush = flush && inputOff == inputEnd;
  650   	if (DeflaterConstants.DEBUGGING)
  651   	  System.err.println("window: ["+blockStart+","+strstart+","
  652   			     +lookahead+"], "+comprFunc+","+canFlush);
  653   	switch (comprFunc)
  654   	  {
  655   	  case DEFLATE_STORED:
  656   	    progress = deflateStored(canFlush, finish);
  657   	    break;
  658   	  case DEFLATE_FAST:
  659   	    progress = deflateFast(canFlush, finish);
  660   	    break;
  661   	  case DEFLATE_SLOW:
  662   	    progress = deflateSlow(canFlush, finish);
  663   	    break;
  664   	  default:
  665   	    throw new InternalError();
  666   	  }
  667         }
  668       while (pending.isFlushed()  /* repeat while we have no pending output */
  669   	   && progress);        /* and progress was made */
  670   
  671       return progress;
  672     }
  673   
  674     public void setInput(byte[] buf, int off, int len)
  675     {
  676       if (inputOff < inputEnd)
  677         throw new IllegalStateException
  678   	("Old input was not completely processed");
  679   
  680       int end = off + len;
  681   
  682       /* We want to throw an ArrayIndexOutOfBoundsException early.  The
  683        * check is very tricky: it also handles integer wrap around.  
  684        */
  685       if (0 > off || off > end || end > buf.length)
  686         throw new ArrayIndexOutOfBoundsException();
  687   
  688       inputBuf = buf;
  689       inputOff = off;
  690       inputEnd = end;
  691     }
  692   
  693     public final boolean needsInput()
  694     {
  695       return inputEnd == inputOff;
  696     }
  697   }

Save This Page
Home » openjdk-7 » java » util » zip » [javadoc | source]