BZip2CompressorOutputStream.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.compressors.bzip2;

  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.util.Arrays;

  23. import org.apache.commons.compress.compressors.CompressorOutputStream;
  24. import org.apache.commons.io.IOUtils;

  25. /**
  26.  * An output stream that compresses into the BZip2 format into another stream.
  27.  *
  28.  * <p>
  29.  * The compression requires large amounts of memory. Thus you should call the {@link #close() close()} method as soon as possible, to force
  30.  * {@code BZip2CompressorOutputStream} to release the allocated memory.
  31.  * </p>
  32.  *
  33.  * <p>
  34.  * You can shrink the amount of allocated memory and maybe raise the compression speed by choosing a lower blocksize, which in turn may cause a lower
  35.  * compression ratio. You can avoid unnecessary memory allocation by avoiding using a blocksize which is bigger than the size of the input.
  36.  * </p>
  37.  *
  38.  * <p>
  39.  * You can compute the memory usage for compressing by the following formula:
  40.  * </p>
  41.  *
  42.  * <pre>
  43.  * &lt;code&gt;400k + (9 * blocksize)&lt;/code&gt;.
  44.  * </pre>
  45.  *
  46.  * <p>
  47.  * To get the memory required for decompression by {@link BZip2CompressorInputStream} use
  48.  * </p>
  49.  *
  50.  * <pre>
  51.  * &lt;code&gt;65k + (5 * blocksize)&lt;/code&gt;.
  52.  * </pre>
  53.  *
  54.  * <table style="width:100%" border="1">
  55.  * <caption>Memory usage by blocksize</caption>
  56.  * <tr>
  57.  * <th colspan="3">Memory usage by blocksize</th>
  58.  * </tr>
  59.  * <tr>
  60.  * <th style="text-align: right">Blocksize</th>
  61.  * <th style="text-align: right">Compression<br>
  62.  * memory usage</th>
  63.  * <th style="text-align: right">Decompression<br>
  64.  * memory usage</th>
  65.  * </tr>
  66.  * <tr>
  67.  * <td style="text-align: right">100k</td>
  68.  * <td style="text-align: right">1300k</td>
  69.  * <td style="text-align: right">565k</td>
  70.  * </tr>
  71.  * <tr>
  72.  * <td style="text-align: right">200k</td>
  73.  * <td style="text-align: right">2200k</td>
  74.  * <td style="text-align: right">1065k</td>
  75.  * </tr>
  76.  * <tr>
  77.  * <td style="text-align: right">300k</td>
  78.  * <td style="text-align: right">3100k</td>
  79.  * <td style="text-align: right">1565k</td>
  80.  * </tr>
  81.  * <tr>
  82.  * <td style="text-align: right">400k</td>
  83.  * <td style="text-align: right">4000k</td>
  84.  * <td style="text-align: right">2065k</td>
  85.  * </tr>
  86.  * <tr>
  87.  * <td style="text-align: right">500k</td>
  88.  * <td style="text-align: right">4900k</td>
  89.  * <td style="text-align: right">2565k</td>
  90.  * </tr>
  91.  * <tr>
  92.  * <td style="text-align: right">600k</td>
  93.  * <td style="text-align: right">5800k</td>
  94.  * <td style="text-align: right">3065k</td>
  95.  * </tr>
  96.  * <tr>
  97.  * <td style="text-align: right">700k</td>
  98.  * <td style="text-align: right">6700k</td>
  99.  * <td style="text-align: right">3565k</td>
  100.  * </tr>
  101.  * <tr>
  102.  * <td style="text-align: right">800k</td>
  103.  * <td style="text-align: right">7600k</td>
  104.  * <td style="text-align: right">4065k</td>
  105.  * </tr>
  106.  * <tr>
  107.  * <td style="text-align: right">900k</td>
  108.  * <td style="text-align: right">8500k</td>
  109.  * <td style="text-align: right">4565k</td>
  110.  * </tr>
  111.  * </table>
  112.  *
  113.  * <p>
  114.  * For decompression {@code BZip2CompressorInputStream} allocates less memory if the bzipped input is smaller than one block.
  115.  * </p>
  116.  *
  117.  * <p>
  118.  * Instances of this class are not threadsafe.
  119.  * </p>
  120.  *
  121.  * <p>
  122.  * TODO: Update to BZip2 1.0.1
  123.  * </p>
  124.  *
  125.  * @NotThreadSafe
  126.  */
  127. public class BZip2CompressorOutputStream extends CompressorOutputStream<OutputStream> implements BZip2Constants {

  128.     static final class Data {

  129.         // with blockSize 900k
  130.         /* maps unsigned byte => "does it occur in block" */
  131.         final boolean[] inUse = new boolean[256]; // 256 byte
  132.         final byte[] unseqToSeq = new byte[256]; // 256 byte
  133.         final int[] mtfFreq = new int[MAX_ALPHA_SIZE]; // 1032 byte
  134.         final byte[] selector = new byte[MAX_SELECTORS]; // 18002 byte
  135.         final byte[] selectorMtf = new byte[MAX_SELECTORS]; // 18002 byte

  136.         final byte[] generateMTFValues_yy = new byte[256]; // 256 byte
  137.         final byte[][] sendMTFValues_len = new byte[N_GROUPS][MAX_ALPHA_SIZE]; // 1548
  138.         // byte
  139.         final int[][] sendMTFValues_rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192
  140.         // byte
  141.         final int[] sendMTFValues_fave = new int[N_GROUPS]; // 24 byte
  142.         final short[] sendMTFValues_cost = new short[N_GROUPS]; // 12 byte
  143.         final int[][] sendMTFValues_code = new int[N_GROUPS][MAX_ALPHA_SIZE]; // 6192
  144.         // byte
  145.         final byte[] sendMTFValues2_pos = new byte[N_GROUPS]; // 6 byte
  146.         final boolean[] sentMTFValues4_inUse16 = new boolean[16]; // 16 byte

  147.         final int[] heap = new int[MAX_ALPHA_SIZE + 2]; // 1040 byte
  148.         final int[] weight = new int[MAX_ALPHA_SIZE * 2]; // 2064 byte
  149.         final int[] parent = new int[MAX_ALPHA_SIZE * 2]; // 2064 byte

  150.         // ------------
  151.         // 333408 byte

  152.         /*
  153.          * holds the RLEd block of original data starting at index 1. After sorting the last byte added to the buffer is at index 0.
  154.          */
  155.         final byte[] block; // 900021 byte
  156.         /*
  157.          * maps index in Burrows-Wheeler transformed block => index of byte in original block
  158.          */
  159.         final int[] fmap; // 3600000 byte
  160.         final char[] sfmap; // 3600000 byte
  161.         // ------------
  162.         // 8433529 byte
  163.         // ============

  164.         /**
  165.          * Index of original line in Burrows-Wheeler table.
  166.          *
  167.          * <p>
  168.          * This is the index in fmap that points to the last byte of the original data.
  169.          * </p>
  170.          */
  171.         int origPtr;

  172.         Data(final int blockSize100k) {
  173.             final int n = blockSize100k * BASEBLOCKSIZE;
  174.             this.block = new byte[n + 1 + NUM_OVERSHOOT_BYTES];
  175.             this.fmap = new int[n];
  176.             this.sfmap = new char[2 * n];
  177.         }

  178.     }

  179.     /**
  180.      * The minimum supported blocksize {@code  == 1}.
  181.      */
  182.     public static final int MIN_BLOCKSIZE = 1;

  183.     /**
  184.      * The maximum supported blocksize {@code  == 9}.
  185.      */
  186.     public static final int MAX_BLOCKSIZE = 9;
  187.     private static final int GREATER_ICOST = 15;

  188.     private static final int LESSER_ICOST = 0;

  189.     /**
  190.      * Chooses a blocksize based on the given length of the data to compress.
  191.      *
  192.      * @return The blocksize, between {@link #MIN_BLOCKSIZE} and {@link #MAX_BLOCKSIZE} both inclusive. For a negative {@code inputLength} this method returns
  193.      *         {@code MAX_BLOCKSIZE} always.
  194.      *
  195.      * @param inputLength The length of the data which will be compressed by {@code BZip2CompressorOutputStream}.
  196.      */
  197.     public static int chooseBlockSize(final long inputLength) {
  198.         return inputLength > 0 ? (int) Math.min(inputLength / 132000 + 1, 9) : MAX_BLOCKSIZE;
  199.     }

  200.     private static void hbAssignCodes(final int[] code, final byte[] length, final int minLen, final int maxLen, final int alphaSize) {
  201.         int vec = 0;
  202.         for (int n = minLen; n <= maxLen; n++) {
  203.             for (int i = 0; i < alphaSize; i++) {
  204.                 if ((length[i] & 0xff) == n) {
  205.                     code[i] = vec;
  206.                     vec++;
  207.                 }
  208.             }
  209.             vec <<= 1;
  210.         }
  211.     }

  212.     private static void hbMakeCodeLengths(final byte[] len, final int[] freq, final Data dat, final int alphaSize, final int maxLen) {
  213.         /*
  214.          * Nodes and heap entries run from 1. Entry 0 for both the heap and nodes is a sentinel.
  215.          */
  216.         final int[] heap = dat.heap;
  217.         final int[] weight = dat.weight;
  218.         final int[] parent = dat.parent;

  219.         for (int i = alphaSize; --i >= 0;) {
  220.             weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
  221.         }

  222.         for (boolean tooLong = true; tooLong;) {
  223.             tooLong = false;

  224.             int nNodes = alphaSize;
  225.             int nHeap = 0;
  226.             heap[0] = 0;
  227.             weight[0] = 0;
  228.             parent[0] = -2;

  229.             for (int i = 1; i <= alphaSize; i++) {
  230.                 parent[i] = -1;
  231.                 nHeap++;
  232.                 heap[nHeap] = i;

  233.                 int zz = nHeap;
  234.                 final int tmp = heap[zz];
  235.                 while (weight[tmp] < weight[heap[zz >> 1]]) {
  236.                     heap[zz] = heap[zz >> 1];
  237.                     zz >>= 1;
  238.                 }
  239.                 heap[zz] = tmp;
  240.             }

  241.             while (nHeap > 1) {
  242.                 final int n1 = heap[1];
  243.                 heap[1] = heap[nHeap];
  244.                 nHeap--;

  245.                 int yy = 0;
  246.                 int zz = 1;
  247.                 int tmp = heap[1];

  248.                 while (true) {
  249.                     yy = zz << 1;

  250.                     if (yy > nHeap) {
  251.                         break;
  252.                     }

  253.                     if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) {
  254.                         yy++;
  255.                     }

  256.                     if (weight[tmp] < weight[heap[yy]]) {
  257.                         break;
  258.                     }

  259.                     heap[zz] = heap[yy];
  260.                     zz = yy;
  261.                 }

  262.                 heap[zz] = tmp;

  263.                 final int n2 = heap[1];
  264.                 heap[1] = heap[nHeap];
  265.                 nHeap--;

  266.                 yy = 0;
  267.                 zz = 1;
  268.                 tmp = heap[1];

  269.                 while (true) {
  270.                     yy = zz << 1;

  271.                     if (yy > nHeap) {
  272.                         break;
  273.                     }

  274.                     if (yy < nHeap && weight[heap[yy + 1]] < weight[heap[yy]]) {
  275.                         yy++;
  276.                     }

  277.                     if (weight[tmp] < weight[heap[yy]]) {
  278.                         break;
  279.                     }

  280.                     heap[zz] = heap[yy];
  281.                     zz = yy;
  282.                 }

  283.                 heap[zz] = tmp;
  284.                 nNodes++;
  285.                 parent[n1] = parent[n2] = nNodes;

  286.                 final int weight_n1 = weight[n1];
  287.                 final int weight_n2 = weight[n2];
  288.                 weight[nNodes] = (weight_n1 & 0xffffff00) + (weight_n2 & 0xffffff00) | 1 + Math.max(weight_n1 & 0x000000ff, weight_n2 & 0x000000ff);

  289.                 parent[nNodes] = -1;
  290.                 nHeap++;
  291.                 heap[nHeap] = nNodes;

  292.                 tmp = 0;
  293.                 zz = nHeap;
  294.                 tmp = heap[zz];
  295.                 final int weight_tmp = weight[tmp];
  296.                 while (weight_tmp < weight[heap[zz >> 1]]) {
  297.                     heap[zz] = heap[zz >> 1];
  298.                     zz >>= 1;
  299.                 }
  300.                 heap[zz] = tmp;

  301.             }

  302.             for (int i = 1; i <= alphaSize; i++) {
  303.                 int j = 0;
  304.                 int k = i;

  305.                 for (int parent_k; (parent_k = parent[k]) >= 0;) {
  306.                     k = parent_k;
  307.                     j++;
  308.                 }

  309.                 len[i - 1] = (byte) j;
  310.                 if (j > maxLen) {
  311.                     tooLong = true;
  312.                 }
  313.             }

  314.             if (tooLong) {
  315.                 for (int i = 1; i < alphaSize; i++) {
  316.                     int j = weight[i] >> 8;
  317.                     j = 1 + (j >> 1);
  318.                     weight[i] = j << 8;
  319.                 }
  320.             }
  321.         }
  322.     }

  323.     /**
  324.      * Index of the last char in the block, so the block size == last + 1.
  325.      */
  326.     private int last;
  327.     /**
  328.      * Always: in the range 0 .. 9. The current block size is 100000 * this number.
  329.      */
  330.     private final int blockSize100k;

  331.     private int bsBuff;

  332.     private int bsLive;

  333.     private final CRC crc = new CRC();
  334.     private int nInUse;

  335.     private int nMTF;
  336.     private int currentChar = -1;
  337.     private int runLength;

  338.     private int combinedCRC;

  339.     private final int allowableBlockSize;
  340.     /**
  341.      * All memory intensive stuff.
  342.      */
  343.     private Data data;

  344.     private BlockSort blockSorter;

  345.     private volatile boolean closed;

  346.     /**
  347.      * Constructs a new {@code BZip2CompressorOutputStream} with a blocksize of 900k.
  348.      *
  349.      * @param out the destination stream.
  350.      *
  351.      * @throws IOException          if an I/O error occurs in the specified stream.
  352.      * @throws NullPointerException if {@code out == null}.
  353.      */
  354.     public BZip2CompressorOutputStream(final OutputStream out) throws IOException {
  355.         this(out, MAX_BLOCKSIZE);
  356.     }

  357.     /**
  358.      * Constructs a new {@code BZip2CompressorOutputStream} with specified blocksize.
  359.      *
  360.      * @param out       the destination stream.
  361.      * @param blockSize the blockSize as 100k units.
  362.      *
  363.      * @throws IOException              if an I/O error occurs in the specified stream.
  364.      * @throws IllegalArgumentException if {@code (blockSize &lt; 1) || (blockSize &gt; 9)}.
  365.      * @throws NullPointerException     if {@code out == null}.
  366.      *
  367.      * @see #MIN_BLOCKSIZE
  368.      * @see #MAX_BLOCKSIZE
  369.      */
  370.     public BZip2CompressorOutputStream(final OutputStream out, final int blockSize) throws IOException {
  371.         super(out);
  372.         if (blockSize < 1) {
  373.             throw new IllegalArgumentException("blockSize(" + blockSize + ") < 1");
  374.         }
  375.         if (blockSize > 9) {
  376.             throw new IllegalArgumentException("blockSize(" + blockSize + ") > 9");
  377.         }
  378.         this.blockSize100k = blockSize;
  379.         /* 20 is just a paranoia constant */
  380.         this.allowableBlockSize = this.blockSize100k * BASEBLOCKSIZE - 20;
  381.         init();
  382.     }

  383.     private void blockSort() {
  384.         blockSorter.blockSort(data, last);
  385.     }

  386.     private void bsFinishedWithStream() throws IOException {
  387.         while (this.bsLive > 0) {
  388.             final int ch = this.bsBuff >> 24;
  389.             this.out.write(ch); // write 8-bit
  390.             this.bsBuff <<= 8;
  391.             this.bsLive -= 8;
  392.         }
  393.     }

  394.     private void bsPutInt(final int u) throws IOException {
  395.         bsW(8, u >> 24 & 0xff);
  396.         bsW(8, u >> 16 & 0xff);
  397.         bsW(8, u >> 8 & 0xff);
  398.         bsW(8, u & 0xff);
  399.     }

  400.     private void bsPutUByte(final int c) throws IOException {
  401.         bsW(8, c);
  402.     }

  403.     private void bsW(final int n, final int v) throws IOException {
  404.         final OutputStream outShadow = this.out;
  405.         int bsLiveShadow = this.bsLive;
  406.         int bsBuffShadow = this.bsBuff;

  407.         while (bsLiveShadow >= 8) {
  408.             outShadow.write(bsBuffShadow >> 24); // write 8-bit
  409.             bsBuffShadow <<= 8;
  410.             bsLiveShadow -= 8;
  411.         }

  412.         this.bsBuff = bsBuffShadow | v << 32 - bsLiveShadow - n;
  413.         this.bsLive = bsLiveShadow + n;
  414.     }

  415.     private void checkClosed() throws IOException {
  416.         if (closed) {
  417.             throw new IOException("Stream closed");
  418.         }
  419.     }

  420.     @Override
  421.     public void close() throws IOException {
  422.         if (!closed) {
  423.             try {
  424.                 finish();
  425.             } finally {
  426.                 IOUtils.close(out);
  427.             }
  428.         }
  429.     }

  430.     private void endBlock() throws IOException {
  431.         final int blockCRC = this.crc.getValue();
  432.         this.combinedCRC = this.combinedCRC << 1 | this.combinedCRC >>> 31;
  433.         this.combinedCRC ^= blockCRC;

  434.         // empty block at end of file
  435.         if (this.last == -1) {
  436.             return;
  437.         }

  438.         /* sort the block and establish posn of original string */
  439.         blockSort();

  440.         /*
  441.          * A 6-byte block header, the value chosen arbitrarily as 0x314159265359 :-). A 32 bit value does not really give a strong enough guarantee that the
  442.          * value will not appear by chance in the compressed data stream. Worst-case probability of this event, for a 900k block, is about 2.0e-3 for 32 bits,
  443.          * 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. For a compressed file of size 100Gb -- about 100000 blocks -- only a 48-bit marker will do. NB: normal
  444.          * compression/ decompression doesn't rely on these statistical properties. They are only important when trying to recover blocks from damaged files.
  445.          */
  446.         bsPutUByte(0x31);
  447.         bsPutUByte(0x41);
  448.         bsPutUByte(0x59);
  449.         bsPutUByte(0x26);
  450.         bsPutUByte(0x53);
  451.         bsPutUByte(0x59);

  452.         /* Now the block's CRC, so it is in a known place. */
  453.         bsPutInt(blockCRC);

  454.         /* Now a single bit indicating no randomization. */
  455.         bsW(1, 0);

  456.         /* Finally, block's contents proper. */
  457.         moveToFrontCodeAndSend();
  458.     }

  459.     private void endCompression() throws IOException {
  460.         /*
  461.          * Now another magic 48-bit number, 0x177245385090, to indicate the end of the last block. (sqrt(pi), if you want to know. I did want to use e, but it
  462.          * contains too much repetition -- 27 18 28 18 28 46 -- for me to feel statistically comfortable. Call me paranoid.)
  463.          */
  464.         bsPutUByte(0x17);
  465.         bsPutUByte(0x72);
  466.         bsPutUByte(0x45);
  467.         bsPutUByte(0x38);
  468.         bsPutUByte(0x50);
  469.         bsPutUByte(0x90);

  470.         bsPutInt(this.combinedCRC);
  471.         bsFinishedWithStream();
  472.     }

  473.     public void finish() throws IOException {
  474.         if (!closed) {
  475.             closed = true;
  476.             try {
  477.                 if (this.runLength > 0) {
  478.                     writeRun();
  479.                 }
  480.                 this.currentChar = -1;
  481.                 endBlock();
  482.                 endCompression();
  483.             } finally {
  484.                 this.blockSorter = null;
  485.                 this.data = null;
  486.             }
  487.         }
  488.     }

  489.     @Override
  490.     public void flush() throws IOException {
  491.         if (out != null) {
  492.             super.flush();
  493.         }
  494.     }

  495.     /*
  496.      * Performs Move-To-Front on the Burrows-Wheeler transformed buffer, storing the MTFed data in data.sfmap in RUNA/RUNB run-length-encoded form.
  497.      *
  498.      * <p>Keeps track of byte frequencies in data.mtfFreq at the same time.</p>
  499.      */
  500.     private void generateMTFValues() {
  501.         final int lastShadow = this.last;
  502.         final Data dataShadow = this.data;
  503.         final boolean[] inUse = dataShadow.inUse;
  504.         final byte[] block = dataShadow.block;
  505.         final int[] fmap = dataShadow.fmap;
  506.         final char[] sfmap = dataShadow.sfmap;
  507.         final int[] mtfFreq = dataShadow.mtfFreq;
  508.         final byte[] unseqToSeq = dataShadow.unseqToSeq;
  509.         final byte[] yy = dataShadow.generateMTFValues_yy;

  510.         // make maps
  511.         int nInUseShadow = 0;
  512.         for (int i = 0; i < 256; i++) {
  513.             if (inUse[i]) {
  514.                 unseqToSeq[i] = (byte) nInUseShadow;
  515.                 nInUseShadow++;
  516.             }
  517.         }
  518.         this.nInUse = nInUseShadow;

  519.         final int eob = nInUseShadow + 1;

  520.         Arrays.fill(mtfFreq, 0, eob + 1, 0);

  521.         for (int i = nInUseShadow; --i >= 0;) {
  522.             yy[i] = (byte) i;
  523.         }

  524.         int wr = 0;
  525.         int zPend = 0;

  526.         for (int i = 0; i <= lastShadow; i++) {
  527.             final byte ll_i = unseqToSeq[block[fmap[i]] & 0xff];
  528.             byte tmp = yy[0];
  529.             int j = 0;

  530.             while (ll_i != tmp) {
  531.                 j++;
  532.                 final byte tmp2 = tmp;
  533.                 tmp = yy[j];
  534.                 yy[j] = tmp2;
  535.             }
  536.             yy[0] = tmp;

  537.             if (j == 0) {
  538.                 zPend++;
  539.             } else {
  540.                 if (zPend > 0) {
  541.                     zPend--;
  542.                     while (true) {
  543.                         if ((zPend & 1) == 0) {
  544.                             sfmap[wr] = RUNA;
  545.                             wr++;
  546.                             mtfFreq[RUNA]++;
  547.                         } else {
  548.                             sfmap[wr] = RUNB;
  549.                             wr++;
  550.                             mtfFreq[RUNB]++;
  551.                         }

  552.                         if (zPend < 2) {
  553.                             break;
  554.                         }
  555.                         zPend = zPend - 2 >> 1;
  556.                     }
  557.                     zPend = 0;
  558.                 }
  559.                 sfmap[wr] = (char) (j + 1);
  560.                 wr++;
  561.                 mtfFreq[j + 1]++;
  562.             }
  563.         }

  564.         if (zPend > 0) {
  565.             zPend--;
  566.             while (true) {
  567.                 if ((zPend & 1) == 0) {
  568.                     sfmap[wr] = RUNA;
  569.                     wr++;
  570.                     mtfFreq[RUNA]++;
  571.                 } else {
  572.                     sfmap[wr] = RUNB;
  573.                     wr++;
  574.                     mtfFreq[RUNB]++;
  575.                 }

  576.                 if (zPend < 2) {
  577.                     break;
  578.                 }
  579.                 zPend = zPend - 2 >> 1;
  580.             }
  581.         }

  582.         sfmap[wr] = (char) eob;
  583.         mtfFreq[eob]++;
  584.         this.nMTF = wr + 1;
  585.     }

  586.     /**
  587.      * Returns the blocksize parameter specified at construction time.
  588.      *
  589.      * @return the blocksize parameter specified at construction time
  590.      */
  591.     public final int getBlockSize() {
  592.         return this.blockSize100k;
  593.     }

  594.     /**
  595.      * Writes magic bytes like BZ on the first position of the stream and bytes indicating the file-format, which is huffmanized, followed by a digit indicating
  596.      * blockSize100k.
  597.      *
  598.      * @throws IOException if the magic bytes could not been written
  599.      */
  600.     private void init() throws IOException {
  601.         bsPutUByte('B');
  602.         bsPutUByte('Z');

  603.         this.data = new Data(this.blockSize100k);
  604.         this.blockSorter = new BlockSort(this.data);

  605.         // huffmanized magic bytes
  606.         bsPutUByte('h');
  607.         bsPutUByte('0' + this.blockSize100k);

  608.         this.combinedCRC = 0;
  609.         initBlock();
  610.     }

  611.     private void initBlock() {
  612.         // blockNo++;
  613.         this.crc.reset();
  614.         this.last = -1;
  615.         // ch = 0;

  616.         final boolean[] inUse = this.data.inUse;
  617.         for (int i = 256; --i >= 0;) {
  618.             inUse[i] = false;
  619.         }

  620.     }

  621.     private void moveToFrontCodeAndSend() throws IOException {
  622.         bsW(24, this.data.origPtr);
  623.         generateMTFValues();
  624.         sendMTFValues();
  625.     }

  626.     private void sendMTFValues() throws IOException {
  627.         final byte[][] len = this.data.sendMTFValues_len;
  628.         final int alphaSize = this.nInUse + 2;

  629.         for (int t = N_GROUPS; --t >= 0;) {
  630.             final byte[] len_t = len[t];
  631.             for (int v = alphaSize; --v >= 0;) {
  632.                 len_t[v] = GREATER_ICOST;
  633.             }
  634.         }

  635.         /* Decide how many coding tables to use */
  636.         // assert (this.nMTF > 0) : this.nMTF;
  637.         final int nGroups = this.nMTF < 200 ? 2 : this.nMTF < 600 ? 3 : this.nMTF < 1200 ? 4 : this.nMTF < 2400 ? 5 : 6;

  638.         /* Generate an initial set of coding tables */
  639.         sendMTFValues0(nGroups, alphaSize);

  640.         /*
  641.          * Iterate up to N_ITERS times to improve the tables.
  642.          */
  643.         final int nSelectors = sendMTFValues1(nGroups, alphaSize);

  644.         /* Compute MTF values for the selectors. */
  645.         sendMTFValues2(nGroups, nSelectors);

  646.         /* Assign actual codes for the tables. */
  647.         sendMTFValues3(nGroups, alphaSize);

  648.         /* Transmit the mapping table. */
  649.         sendMTFValues4();

  650.         /* Now the selectors. */
  651.         sendMTFValues5(nGroups, nSelectors);

  652.         /* Now the coding tables. */
  653.         sendMTFValues6(nGroups, alphaSize);

  654.         /* And finally, the block data proper */
  655.         sendMTFValues7();
  656.     }

  657.     private void sendMTFValues0(final int nGroups, final int alphaSize) {
  658.         final byte[][] len = this.data.sendMTFValues_len;
  659.         final int[] mtfFreq = this.data.mtfFreq;

  660.         int remF = this.nMTF;
  661.         int gs = 0;

  662.         for (int nPart = nGroups; nPart > 0; nPart--) {
  663.             final int tFreq = remF / nPart;
  664.             int ge = gs - 1;
  665.             int aFreq = 0;

  666.             for (final int a = alphaSize - 1; aFreq < tFreq && ge < a;) {
  667.                 aFreq += mtfFreq[++ge];
  668.             }

  669.             if (ge > gs && nPart != nGroups && nPart != 1 && (nGroups - nPart & 1) != 0) {
  670.                 aFreq -= mtfFreq[ge--];
  671.             }

  672.             final byte[] len_np = len[nPart - 1];
  673.             for (int v = alphaSize; --v >= 0;) {
  674.                 if (v >= gs && v <= ge) {
  675.                     len_np[v] = LESSER_ICOST;
  676.                 } else {
  677.                     len_np[v] = GREATER_ICOST;
  678.                 }
  679.             }

  680.             gs = ge + 1;
  681.             remF -= aFreq;
  682.         }
  683.     }

  684.     private int sendMTFValues1(final int nGroups, final int alphaSize) {
  685.         final Data dataShadow = this.data;
  686.         final int[][] rfreq = dataShadow.sendMTFValues_rfreq;
  687.         final int[] fave = dataShadow.sendMTFValues_fave;
  688.         final short[] cost = dataShadow.sendMTFValues_cost;
  689.         final char[] sfmap = dataShadow.sfmap;
  690.         final byte[] selector = dataShadow.selector;
  691.         final byte[][] len = dataShadow.sendMTFValues_len;
  692.         final byte[] len_0 = len[0];
  693.         final byte[] len_1 = len[1];
  694.         final byte[] len_2 = len[2];
  695.         final byte[] len_3 = len[3];
  696.         final byte[] len_4 = len[4];
  697.         final byte[] len_5 = len[5];
  698.         final int nMTFShadow = this.nMTF;

  699.         int nSelectors = 0;

  700.         for (int iter = 0; iter < N_ITERS; iter++) {
  701.             for (int t = nGroups; --t >= 0;) {
  702.                 fave[t] = 0;
  703.                 final int[] rfreqt = rfreq[t];
  704.                 for (int i = alphaSize; --i >= 0;) {
  705.                     rfreqt[i] = 0;
  706.                 }
  707.             }

  708.             nSelectors = 0;

  709.             for (int gs = 0; gs < this.nMTF;) {
  710.                 // Set group start & end marks.

  711.                 // Calculate the cost of this group as coded by each of the
  712.                 // coding tables.

  713.                 final int ge = Math.min(gs + G_SIZE - 1, nMTFShadow - 1);

  714.                 final byte mask = (byte) 0xff;
  715.                 if (nGroups == N_GROUPS) {
  716.                     // unrolled version of the else-block

  717.                     short cost0 = 0;
  718.                     short cost1 = 0;
  719.                     short cost2 = 0;
  720.                     short cost3 = 0;
  721.                     short cost4 = 0;
  722.                     short cost5 = 0;

  723.                     for (int i = gs; i <= ge; i++) {
  724.                         final int icv = sfmap[i];
  725.                         cost0 += (short) (len_0[icv] & mask);
  726.                         cost1 += (short) (len_1[icv] & mask);
  727.                         cost2 += (short) (len_2[icv] & mask);
  728.                         cost3 += (short) (len_3[icv] & mask);
  729.                         cost4 += (short) (len_4[icv] & mask);
  730.                         cost5 += (short) (len_5[icv] & mask);
  731.                     }

  732.                     cost[0] = cost0;
  733.                     cost[1] = cost1;
  734.                     cost[2] = cost2;
  735.                     cost[3] = cost3;
  736.                     cost[4] = cost4;
  737.                     cost[5] = cost5;

  738.                 } else {
  739.                     for (int t = nGroups; --t >= 0;) {
  740.                         cost[t] = 0;
  741.                     }

  742.                     for (int i = gs; i <= ge; i++) {
  743.                         final int icv = sfmap[i];
  744.                         for (int t = nGroups; --t >= 0;) {
  745.                             cost[t] += (short) (len[t][icv] & mask);
  746.                         }
  747.                     }
  748.                 }

  749.                 /*
  750.                  * Find the coding table which is best for this group, and record its identity in the selector table.
  751.                  */
  752.                 int bt = -1;
  753.                 for (int t = nGroups, bc = 999999999; --t >= 0;) {
  754.                     final int cost_t = cost[t];
  755.                     if (cost_t < bc) {
  756.                         bc = cost_t;
  757.                         bt = t;
  758.                     }
  759.                 }

  760.                 fave[bt]++;
  761.                 selector[nSelectors] = (byte) bt;
  762.                 nSelectors++;

  763.                 /*
  764.                  * Increment the symbol frequencies for the selected table.
  765.                  */
  766.                 final int[] rfreq_bt = rfreq[bt];
  767.                 for (int i = gs; i <= ge; i++) {
  768.                     rfreq_bt[sfmap[i]]++;
  769.                 }

  770.                 gs = ge + 1;
  771.             }

  772.             /*
  773.              * Recompute the tables based on the accumulated frequencies.
  774.              */
  775.             for (int t = 0; t < nGroups; t++) {
  776.                 hbMakeCodeLengths(len[t], rfreq[t], this.data, alphaSize, 20);
  777.             }
  778.         }

  779.         return nSelectors;
  780.     }

  781.     private void sendMTFValues2(final int nGroups, final int nSelectors) {
  782.         // assert (nGroups < 8) : nGroups;

  783.         final Data dataShadow = this.data;
  784.         final byte[] pos = dataShadow.sendMTFValues2_pos;

  785.         for (int i = nGroups; --i >= 0;) {
  786.             pos[i] = (byte) i;
  787.         }

  788.         for (int i = 0; i < nSelectors; i++) {
  789.             final byte ll_i = dataShadow.selector[i];
  790.             byte tmp = pos[0];
  791.             int j = 0;

  792.             while (ll_i != tmp) {
  793.                 j++;
  794.                 final byte tmp2 = tmp;
  795.                 tmp = pos[j];
  796.                 pos[j] = tmp2;
  797.             }

  798.             pos[0] = tmp;
  799.             dataShadow.selectorMtf[i] = (byte) j;
  800.         }
  801.     }

  802.     private void sendMTFValues3(final int nGroups, final int alphaSize) {
  803.         final int[][] code = this.data.sendMTFValues_code;
  804.         final byte[][] len = this.data.sendMTFValues_len;

  805.         for (int t = 0; t < nGroups; t++) {
  806.             int minLen = 32;
  807.             int maxLen = 0;
  808.             final byte[] len_t = len[t];
  809.             for (int i = alphaSize; --i >= 0;) {
  810.                 final int l = len_t[i] & 0xff;
  811.                 if (l > maxLen) {
  812.                     maxLen = l;
  813.                 }
  814.                 if (l < minLen) {
  815.                     minLen = l;
  816.                 }
  817.             }

  818.             // assert (maxLen <= 20) : maxLen;
  819.             // assert (minLen >= 1) : minLen;

  820.             hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
  821.         }
  822.     }

  823.     private void sendMTFValues4() throws IOException {
  824.         final boolean[] inUse = this.data.inUse;
  825.         final boolean[] inUse16 = this.data.sentMTFValues4_inUse16;

  826.         for (int i = 16; --i >= 0;) {
  827.             inUse16[i] = false;
  828.             final int i16 = i * 16;
  829.             for (int j = 16; --j >= 0;) {
  830.                 if (inUse[i16 + j]) {
  831.                     inUse16[i] = true;
  832.                     break;
  833.                 }
  834.             }
  835.         }

  836.         for (int i = 0; i < 16; i++) {
  837.             bsW(1, inUse16[i] ? 1 : 0);
  838.         }

  839.         final OutputStream outShadow = this.out;
  840.         int bsLiveShadow = this.bsLive;
  841.         int bsBuffShadow = this.bsBuff;

  842.         for (int i = 0; i < 16; i++) {
  843.             if (inUse16[i]) {
  844.                 final int i16 = i * 16;
  845.                 for (int j = 0; j < 16; j++) {
  846.                     // inlined: bsW(1, inUse[i16 + j] ? 1 : 0);
  847.                     while (bsLiveShadow >= 8) {
  848.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  849.                         bsBuffShadow <<= 8;
  850.                         bsLiveShadow -= 8;
  851.                     }
  852.                     if (inUse[i16 + j]) {
  853.                         bsBuffShadow |= 1 << 32 - bsLiveShadow - 1;
  854.                     }
  855.                     bsLiveShadow++;
  856.                 }
  857.             }
  858.         }

  859.         this.bsBuff = bsBuffShadow;
  860.         this.bsLive = bsLiveShadow;
  861.     }

  862.     private void sendMTFValues5(final int nGroups, final int nSelectors) throws IOException {
  863.         bsW(3, nGroups);
  864.         bsW(15, nSelectors);

  865.         final OutputStream outShadow = this.out;
  866.         final byte[] selectorMtf = this.data.selectorMtf;

  867.         int bsLiveShadow = this.bsLive;
  868.         int bsBuffShadow = this.bsBuff;

  869.         for (int i = 0; i < nSelectors; i++) {
  870.             for (int j = 0, hj = selectorMtf[i] & 0xff; j < hj; j++) {
  871.                 // inlined: bsW(1, 1);
  872.                 while (bsLiveShadow >= 8) {
  873.                     outShadow.write(bsBuffShadow >> 24);
  874.                     bsBuffShadow <<= 8;
  875.                     bsLiveShadow -= 8;
  876.                 }
  877.                 bsBuffShadow |= 1 << 32 - bsLiveShadow - 1;
  878.                 bsLiveShadow++;
  879.             }

  880.             // inlined: bsW(1, 0);
  881.             while (bsLiveShadow >= 8) {
  882.                 outShadow.write(bsBuffShadow >> 24);
  883.                 bsBuffShadow <<= 8;
  884.                 bsLiveShadow -= 8;
  885.             }
  886.             // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
  887.             bsLiveShadow++;
  888.         }

  889.         this.bsBuff = bsBuffShadow;
  890.         this.bsLive = bsLiveShadow;
  891.     }

  892.     private void sendMTFValues6(final int nGroups, final int alphaSize) throws IOException {
  893.         final byte[][] len = this.data.sendMTFValues_len;
  894.         final OutputStream outShadow = this.out;

  895.         int bsLiveShadow = this.bsLive;
  896.         int bsBuffShadow = this.bsBuff;

  897.         for (int t = 0; t < nGroups; t++) {
  898.             final byte[] len_t = len[t];
  899.             int curr = len_t[0] & 0xff;

  900.             // inlined: bsW(5, curr);
  901.             while (bsLiveShadow >= 8) {
  902.                 outShadow.write(bsBuffShadow >> 24); // write 8-bit
  903.                 bsBuffShadow <<= 8;
  904.                 bsLiveShadow -= 8;
  905.             }
  906.             bsBuffShadow |= curr << 32 - bsLiveShadow - 5;
  907.             bsLiveShadow += 5;

  908.             for (int i = 0; i < alphaSize; i++) {
  909.                 final int lti = len_t[i] & 0xff;
  910.                 while (curr < lti) {
  911.                     // inlined: bsW(2, 2);
  912.                     while (bsLiveShadow >= 8) {
  913.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  914.                         bsBuffShadow <<= 8;
  915.                         bsLiveShadow -= 8;
  916.                     }
  917.                     bsBuffShadow |= 2 << 32 - bsLiveShadow - 2;
  918.                     bsLiveShadow += 2;

  919.                     curr++; /* 10 */
  920.                 }

  921.                 while (curr > lti) {
  922.                     // inlined: bsW(2, 3);
  923.                     while (bsLiveShadow >= 8) {
  924.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  925.                         bsBuffShadow <<= 8;
  926.                         bsLiveShadow -= 8;
  927.                     }
  928.                     bsBuffShadow |= 3 << 32 - bsLiveShadow - 2;
  929.                     bsLiveShadow += 2;

  930.                     curr--; /* 11 */
  931.                 }

  932.                 // inlined: bsW(1, 0);
  933.                 while (bsLiveShadow >= 8) {
  934.                     outShadow.write(bsBuffShadow >> 24); // write 8-bit
  935.                     bsBuffShadow <<= 8;
  936.                     bsLiveShadow -= 8;
  937.                 }
  938.                 // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
  939.                 bsLiveShadow++;
  940.             }
  941.         }

  942.         this.bsBuff = bsBuffShadow;
  943.         this.bsLive = bsLiveShadow;
  944.     }

  945.     private void sendMTFValues7() throws IOException {
  946.         final Data dataShadow = this.data;
  947.         final byte[][] len = dataShadow.sendMTFValues_len;
  948.         final int[][] code = dataShadow.sendMTFValues_code;
  949.         final OutputStream outShadow = this.out;
  950.         final byte[] selector = dataShadow.selector;
  951.         final char[] sfmap = dataShadow.sfmap;
  952.         final int nMTFShadow = this.nMTF;

  953.         int selCtr = 0;

  954.         int bsLiveShadow = this.bsLive;
  955.         int bsBuffShadow = this.bsBuff;

  956.         for (int gs = 0; gs < nMTFShadow;) {
  957.             final int ge = Math.min(gs + G_SIZE - 1, nMTFShadow - 1);
  958.             final int selector_selCtr = selector[selCtr] & 0xff;
  959.             final int[] code_selCtr = code[selector_selCtr];
  960.             final byte[] len_selCtr = len[selector_selCtr];

  961.             while (gs <= ge) {
  962.                 final int sfmap_i = sfmap[gs];

  963.                 //
  964.                 // inlined: bsW(len_selCtr[sfmap_i] & 0xff,
  965.                 // code_selCtr[sfmap_i]);
  966.                 //
  967.                 while (bsLiveShadow >= 8) {
  968.                     outShadow.write(bsBuffShadow >> 24);
  969.                     bsBuffShadow <<= 8;
  970.                     bsLiveShadow -= 8;
  971.                 }
  972.                 final int n = len_selCtr[sfmap_i] & 0xFF;
  973.                 bsBuffShadow |= code_selCtr[sfmap_i] << 32 - bsLiveShadow - n;
  974.                 bsLiveShadow += n;

  975.                 gs++;
  976.             }

  977.             gs = ge + 1;
  978.             selCtr++;
  979.         }

  980.         this.bsBuff = bsBuffShadow;
  981.         this.bsLive = bsLiveShadow;
  982.     }

  983.     @Override
  984.     public void write(final byte[] buf, int offs, final int len) throws IOException {
  985.         if (offs < 0) {
  986.             throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
  987.         }
  988.         if (len < 0) {
  989.             throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
  990.         }
  991.         if (offs + len > buf.length) {
  992.             throw new IndexOutOfBoundsException("offs(" + offs + ") + len(" + len + ") > buf.length(" + buf.length + ").");
  993.         }
  994.         checkClosed();
  995.         for (final int hi = offs + len; offs < hi;) {
  996.             write0(buf[offs++]);
  997.         }
  998.     }

  999.     @Override
  1000.     public void write(final int b) throws IOException {
  1001.         checkClosed();
  1002.         write0(b);
  1003.     }

  1004.     /**
  1005.      * Keeps track of the last bytes written and implicitly performs run-length encoding as the first step of the bzip2 algorithm.
  1006.      */
  1007.     private void write0(int b) throws IOException {
  1008.         if (this.currentChar != -1) {
  1009.             b &= 0xff;
  1010.             if (this.currentChar == b) {
  1011.                 if (++this.runLength > 254) {
  1012.                     writeRun();
  1013.                     this.currentChar = -1;
  1014.                     this.runLength = 0;
  1015.                 }
  1016.                 // else nothing to do
  1017.             } else {
  1018.                 writeRun();
  1019.                 this.runLength = 1;
  1020.                 this.currentChar = b;
  1021.             }
  1022.         } else {
  1023.             this.currentChar = b & 0xff;
  1024.             this.runLength++;
  1025.         }
  1026.     }

  1027.     /**
  1028.      * Writes the current byte to the buffer, run-length encoding it if it has been repeated at least four times (the first step RLEs sequences of four
  1029.      * identical bytes).
  1030.      *
  1031.      * <p>
  1032.      * Flushes the current block before writing data if it is full.
  1033.      * </p>
  1034.      *
  1035.      * <p>
  1036.      * "write to the buffer" means adding to data.buffer starting two steps "after" this.last - initially starting at index 1 (not 0) - and updating this.last
  1037.      * to point to the last index written minus 1.
  1038.      * </p>
  1039.      */
  1040.     private void writeRun() throws IOException {
  1041.         final int lastShadow = this.last;

  1042.         if (lastShadow < this.allowableBlockSize) {
  1043.             final int currentCharShadow = this.currentChar;
  1044.             final Data dataShadow = this.data;
  1045.             dataShadow.inUse[currentCharShadow] = true;
  1046.             final byte ch = (byte) currentCharShadow;

  1047.             int runLengthShadow = this.runLength;
  1048.             this.crc.update(currentCharShadow, runLengthShadow);

  1049.             switch (runLengthShadow) {
  1050.             case 1:
  1051.                 dataShadow.block[lastShadow + 2] = ch;
  1052.                 this.last = lastShadow + 1;
  1053.                 break;

  1054.             case 2:
  1055.                 dataShadow.block[lastShadow + 2] = ch;
  1056.                 dataShadow.block[lastShadow + 3] = ch;
  1057.                 this.last = lastShadow + 2;
  1058.                 break;

  1059.             case 3: {
  1060.                 final byte[] block = dataShadow.block;
  1061.                 block[lastShadow + 2] = ch;
  1062.                 block[lastShadow + 3] = ch;
  1063.                 block[lastShadow + 4] = ch;
  1064.                 this.last = lastShadow + 3;
  1065.             }
  1066.                 break;

  1067.             default: {
  1068.                 runLengthShadow -= 4;
  1069.                 dataShadow.inUse[runLengthShadow] = true;
  1070.                 final byte[] block = dataShadow.block;
  1071.                 block[lastShadow + 2] = ch;
  1072.                 block[lastShadow + 3] = ch;
  1073.                 block[lastShadow + 4] = ch;
  1074.                 block[lastShadow + 5] = ch;
  1075.                 block[lastShadow + 6] = (byte) runLengthShadow;
  1076.                 this.last = lastShadow + 5;
  1077.             }
  1078.                 break;

  1079.             }
  1080.         } else {
  1081.             endBlock();
  1082.             initBlock();
  1083.             writeRun();
  1084.         }
  1085.     }

  1086. }