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

  127.     static final class Data {

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

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

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

  149.         // ------------
  150.         // 333408 byte

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

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

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

  177.     }

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

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

  187.     private static final int LESSER_ICOST = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  261.                 heap[zz] = tmp;

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

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

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

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

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

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

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

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

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

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

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

  300.             }

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

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

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

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

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

  330.     private int bsBuff;

  331.     private int bsLive;

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

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

  337.     private int combinedCRC;

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

  343.     private BlockSort blockSorter;

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

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

  378.     private void blockSort() {
  379.         blockSorter.blockSort(data, last);
  380.     }

  381.     private void bsFinishedWithStream() throws IOException {
  382.         while (this.bsLive > 0) {
  383.             final int ch = this.bsBuff >> 24;
  384.             this.out.write(ch); // write 8-bit
  385.             this.bsBuff <<= 8;
  386.             this.bsLive -= 8;
  387.         }
  388.     }

  389.     private void bsPutInt(final int u) throws IOException {
  390.         bsW(8, u >> 24 & 0xff);
  391.         bsW(8, u >> 16 & 0xff);
  392.         bsW(8, u >> 8 & 0xff);
  393.         bsW(8, u & 0xff);
  394.     }

  395.     private void bsPutUByte(final int c) throws IOException {
  396.         bsW(8, c);
  397.     }

  398.     private void bsW(final int n, final int v) throws IOException {
  399.         final OutputStream outShadow = this.out;
  400.         int bsLiveShadow = this.bsLive;
  401.         int bsBuffShadow = this.bsBuff;

  402.         while (bsLiveShadow >= 8) {
  403.             outShadow.write(bsBuffShadow >> 24); // write 8-bit
  404.             bsBuffShadow <<= 8;
  405.             bsLiveShadow -= 8;
  406.         }

  407.         this.bsBuff = bsBuffShadow | v << 32 - bsLiveShadow - n;
  408.         this.bsLive = bsLiveShadow + n;
  409.     }

  410.     @Override
  411.     public void close() throws IOException {
  412.         if (!isClosed()) {
  413.             try {
  414.                 finish();
  415.             } finally {
  416.                 super.close();
  417.             }
  418.         }
  419.     }

  420.     private void endBlock() throws IOException {
  421.         final int blockCRC = this.crc.getValue();
  422.         this.combinedCRC = this.combinedCRC << 1 | this.combinedCRC >>> 31;
  423.         this.combinedCRC ^= blockCRC;

  424.         // empty block at end of file
  425.         if (this.last == -1) {
  426.             return;
  427.         }

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

  430.         /*
  431.          * 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
  432.          * 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,
  433.          * 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
  434.          * compression/ decompression doesn't rely on these statistical properties. They are only important when trying to recover blocks from damaged files.
  435.          */
  436.         bsPutUByte(0x31);
  437.         bsPutUByte(0x41);
  438.         bsPutUByte(0x59);
  439.         bsPutUByte(0x26);
  440.         bsPutUByte(0x53);
  441.         bsPutUByte(0x59);

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

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

  446.         /* Finally, block's contents proper. */
  447.         moveToFrontCodeAndSend();
  448.     }

  449.     private void endCompression() throws IOException {
  450.         /*
  451.          * 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
  452.          * contains too much repetition -- 27 18 28 18 28 46 -- for me to feel statistically comfortable. Call me paranoid.)
  453.          */
  454.         bsPutUByte(0x17);
  455.         bsPutUByte(0x72);
  456.         bsPutUByte(0x45);
  457.         bsPutUByte(0x38);
  458.         bsPutUByte(0x50);
  459.         bsPutUByte(0x90);

  460.         bsPutInt(this.combinedCRC);
  461.         bsFinishedWithStream();
  462.     }

  463.     @Override
  464.     public void finish() throws IOException {
  465.         if (!isClosed()) {
  466.             try {
  467.                 if (this.runLength > 0) {
  468.                     writeRun();
  469.                 }
  470.                 this.currentChar = -1;
  471.                 endBlock();
  472.                 endCompression();
  473.             } finally {
  474.                 this.blockSorter = null;
  475.                 this.data = null;
  476.             }
  477.         }
  478.     }

  479.     @Override
  480.     public void flush() throws IOException {
  481.         if (out != null) {
  482.             super.flush();
  483.         }
  484.     }

  485.     /*
  486.      * Performs Move-To-Front on the Burrows-Wheeler transformed buffer, storing the MTFed data in data.sfmap in RUNA/RUNB run-length-encoded form.
  487.      *
  488.      * <p>Keeps track of byte frequencies in data.mtfFreq at the same time.</p>
  489.      */
  490.     private void generateMTFValues() {
  491.         final int lastShadow = this.last;
  492.         final Data dataShadow = this.data;
  493.         final boolean[] inUse = dataShadow.inUse;
  494.         final byte[] block = dataShadow.block;
  495.         final int[] fmap = dataShadow.fmap;
  496.         final char[] sfmap = dataShadow.sfmap;
  497.         final int[] mtfFreq = dataShadow.mtfFreq;
  498.         final byte[] unseqToSeq = dataShadow.unseqToSeq;
  499.         final byte[] yy = dataShadow.generateMTFValues_yy;

  500.         // make maps
  501.         int nInUseShadow = 0;
  502.         for (int i = 0; i < 256; i++) {
  503.             if (inUse[i]) {
  504.                 unseqToSeq[i] = (byte) nInUseShadow;
  505.                 nInUseShadow++;
  506.             }
  507.         }
  508.         this.nInUse = nInUseShadow;

  509.         final int eob = nInUseShadow + 1;

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

  511.         for (int i = nInUseShadow; --i >= 0;) {
  512.             yy[i] = (byte) i;
  513.         }

  514.         int wr = 0;
  515.         int zPend = 0;

  516.         for (int i = 0; i <= lastShadow; i++) {
  517.             final byte ll_i = unseqToSeq[block[fmap[i]] & 0xff];
  518.             byte tmp = yy[0];
  519.             int j = 0;

  520.             while (ll_i != tmp) {
  521.                 j++;
  522.                 final byte tmp2 = tmp;
  523.                 tmp = yy[j];
  524.                 yy[j] = tmp2;
  525.             }
  526.             yy[0] = tmp;

  527.             if (j == 0) {
  528.                 zPend++;
  529.             } else {
  530.                 if (zPend > 0) {
  531.                     zPend--;
  532.                     while (true) {
  533.                         if ((zPend & 1) == 0) {
  534.                             sfmap[wr] = RUNA;
  535.                             wr++;
  536.                             mtfFreq[RUNA]++;
  537.                         } else {
  538.                             sfmap[wr] = RUNB;
  539.                             wr++;
  540.                             mtfFreq[RUNB]++;
  541.                         }

  542.                         if (zPend < 2) {
  543.                             break;
  544.                         }
  545.                         zPend = zPend - 2 >> 1;
  546.                     }
  547.                     zPend = 0;
  548.                 }
  549.                 sfmap[wr] = (char) (j + 1);
  550.                 wr++;
  551.                 mtfFreq[j + 1]++;
  552.             }
  553.         }

  554.         if (zPend > 0) {
  555.             zPend--;
  556.             while (true) {
  557.                 if ((zPend & 1) == 0) {
  558.                     sfmap[wr] = RUNA;
  559.                     wr++;
  560.                     mtfFreq[RUNA]++;
  561.                 } else {
  562.                     sfmap[wr] = RUNB;
  563.                     wr++;
  564.                     mtfFreq[RUNB]++;
  565.                 }

  566.                 if (zPend < 2) {
  567.                     break;
  568.                 }
  569.                 zPend = zPend - 2 >> 1;
  570.             }
  571.         }

  572.         sfmap[wr] = (char) eob;
  573.         mtfFreq[eob]++;
  574.         this.nMTF = wr + 1;
  575.     }

  576.     /**
  577.      * Returns the blocksize parameter specified at construction time.
  578.      *
  579.      * @return the blocksize parameter specified at construction time
  580.      */
  581.     public final int getBlockSize() {
  582.         return this.blockSize100k;
  583.     }

  584.     /**
  585.      * 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
  586.      * blockSize100k.
  587.      *
  588.      * @throws IOException if the magic bytes could not been written
  589.      */
  590.     private void init() throws IOException {
  591.         bsPutUByte('B');
  592.         bsPutUByte('Z');

  593.         this.data = new Data(this.blockSize100k);
  594.         this.blockSorter = new BlockSort(this.data);

  595.         // huffmanized magic bytes
  596.         bsPutUByte('h');
  597.         bsPutUByte('0' + this.blockSize100k);

  598.         this.combinedCRC = 0;
  599.         initBlock();
  600.     }

  601.     private void initBlock() {
  602.         // blockNo++;
  603.         this.crc.reset();
  604.         this.last = -1;
  605.         // ch = 0;

  606.         final boolean[] inUse = this.data.inUse;
  607.         for (int i = 256; --i >= 0;) {
  608.             inUse[i] = false;
  609.         }

  610.     }

  611.     private void moveToFrontCodeAndSend() throws IOException {
  612.         bsW(24, this.data.origPtr);
  613.         generateMTFValues();
  614.         sendMTFValues();
  615.     }

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

  619.         for (int t = N_GROUPS; --t >= 0;) {
  620.             final byte[] len_t = len[t];
  621.             for (int v = alphaSize; --v >= 0;) {
  622.                 len_t[v] = GREATER_ICOST;
  623.             }
  624.         }

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

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

  630.         /*
  631.          * Iterate up to N_ITERS times to improve the tables.
  632.          */
  633.         final int nSelectors = sendMTFValues1(nGroups, alphaSize);

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

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

  638.         /* Transmit the mapping table. */
  639.         sendMTFValues4();

  640.         /* Now the selectors. */
  641.         sendMTFValues5(nGroups, nSelectors);

  642.         /* Now the coding tables. */
  643.         sendMTFValues6(nGroups, alphaSize);

  644.         /* And finally, the block data proper */
  645.         sendMTFValues7();
  646.     }

  647.     private void sendMTFValues0(final int nGroups, final int alphaSize) {
  648.         final byte[][] len = this.data.sendMTFValues_len;
  649.         final int[] mtfFreq = this.data.mtfFreq;

  650.         int remF = this.nMTF;
  651.         int gs = 0;

  652.         for (int nPart = nGroups; nPart > 0; nPart--) {
  653.             final int tFreq = remF / nPart;
  654.             int ge = gs - 1;
  655.             int aFreq = 0;

  656.             for (final int a = alphaSize - 1; aFreq < tFreq && ge < a;) {
  657.                 aFreq += mtfFreq[++ge];
  658.             }

  659.             if (ge > gs && nPart != nGroups && nPart != 1 && (nGroups - nPart & 1) != 0) {
  660.                 aFreq -= mtfFreq[ge--];
  661.             }

  662.             final byte[] len_np = len[nPart - 1];
  663.             for (int v = alphaSize; --v >= 0;) {
  664.                 if (v >= gs && v <= ge) {
  665.                     len_np[v] = LESSER_ICOST;
  666.                 } else {
  667.                     len_np[v] = GREATER_ICOST;
  668.                 }
  669.             }

  670.             gs = ge + 1;
  671.             remF -= aFreq;
  672.         }
  673.     }

  674.     private int sendMTFValues1(final int nGroups, final int alphaSize) {
  675.         final Data dataShadow = this.data;
  676.         final int[][] rfreq = dataShadow.sendMTFValues_rfreq;
  677.         final int[] fave = dataShadow.sendMTFValues_fave;
  678.         final short[] cost = dataShadow.sendMTFValues_cost;
  679.         final char[] sfmap = dataShadow.sfmap;
  680.         final byte[] selector = dataShadow.selector;
  681.         final byte[][] len = dataShadow.sendMTFValues_len;
  682.         final byte[] len_0 = len[0];
  683.         final byte[] len_1 = len[1];
  684.         final byte[] len_2 = len[2];
  685.         final byte[] len_3 = len[3];
  686.         final byte[] len_4 = len[4];
  687.         final byte[] len_5 = len[5];
  688.         final int nMTFShadow = this.nMTF;

  689.         int nSelectors = 0;

  690.         for (int iter = 0; iter < N_ITERS; iter++) {
  691.             for (int t = nGroups; --t >= 0;) {
  692.                 fave[t] = 0;
  693.                 final int[] rfreqt = rfreq[t];
  694.                 for (int i = alphaSize; --i >= 0;) {
  695.                     rfreqt[i] = 0;
  696.                 }
  697.             }

  698.             nSelectors = 0;

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

  701.                 // Calculate the cost of this group as coded by each of the
  702.                 // coding tables.

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

  704.                 final byte mask = (byte) 0xff;
  705.                 if (nGroups == N_GROUPS) {
  706.                     // unrolled version of the else-block

  707.                     short cost0 = 0;
  708.                     short cost1 = 0;
  709.                     short cost2 = 0;
  710.                     short cost3 = 0;
  711.                     short cost4 = 0;
  712.                     short cost5 = 0;

  713.                     for (int i = gs; i <= ge; i++) {
  714.                         final int icv = sfmap[i];
  715.                         cost0 += (short) (len_0[icv] & mask);
  716.                         cost1 += (short) (len_1[icv] & mask);
  717.                         cost2 += (short) (len_2[icv] & mask);
  718.                         cost3 += (short) (len_3[icv] & mask);
  719.                         cost4 += (short) (len_4[icv] & mask);
  720.                         cost5 += (short) (len_5[icv] & mask);
  721.                     }

  722.                     cost[0] = cost0;
  723.                     cost[1] = cost1;
  724.                     cost[2] = cost2;
  725.                     cost[3] = cost3;
  726.                     cost[4] = cost4;
  727.                     cost[5] = cost5;

  728.                 } else {
  729.                     for (int t = nGroups; --t >= 0;) {
  730.                         cost[t] = 0;
  731.                     }

  732.                     for (int i = gs; i <= ge; i++) {
  733.                         final int icv = sfmap[i];
  734.                         for (int t = nGroups; --t >= 0;) {
  735.                             cost[t] += (short) (len[t][icv] & mask);
  736.                         }
  737.                     }
  738.                 }

  739.                 /*
  740.                  * Find the coding table which is best for this group, and record its identity in the selector table.
  741.                  */
  742.                 int bt = -1;
  743.                 for (int t = nGroups, bc = 999999999; --t >= 0;) {
  744.                     final int cost_t = cost[t];
  745.                     if (cost_t < bc) {
  746.                         bc = cost_t;
  747.                         bt = t;
  748.                     }
  749.                 }

  750.                 fave[bt]++;
  751.                 selector[nSelectors] = (byte) bt;
  752.                 nSelectors++;

  753.                 /*
  754.                  * Increment the symbol frequencies for the selected table.
  755.                  */
  756.                 final int[] rfreq_bt = rfreq[bt];
  757.                 for (int i = gs; i <= ge; i++) {
  758.                     rfreq_bt[sfmap[i]]++;
  759.                 }

  760.                 gs = ge + 1;
  761.             }

  762.             /*
  763.              * Recompute the tables based on the accumulated frequencies.
  764.              */
  765.             for (int t = 0; t < nGroups; t++) {
  766.                 hbMakeCodeLengths(len[t], rfreq[t], this.data, alphaSize, 20);
  767.             }
  768.         }

  769.         return nSelectors;
  770.     }

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

  773.         final Data dataShadow = this.data;
  774.         final byte[] pos = dataShadow.sendMTFValues2_pos;

  775.         for (int i = nGroups; --i >= 0;) {
  776.             pos[i] = (byte) i;
  777.         }

  778.         for (int i = 0; i < nSelectors; i++) {
  779.             final byte ll_i = dataShadow.selector[i];
  780.             byte tmp = pos[0];
  781.             int j = 0;

  782.             while (ll_i != tmp) {
  783.                 j++;
  784.                 final byte tmp2 = tmp;
  785.                 tmp = pos[j];
  786.                 pos[j] = tmp2;
  787.             }

  788.             pos[0] = tmp;
  789.             dataShadow.selectorMtf[i] = (byte) j;
  790.         }
  791.     }

  792.     private void sendMTFValues3(final int nGroups, final int alphaSize) {
  793.         final int[][] code = this.data.sendMTFValues_code;
  794.         final byte[][] len = this.data.sendMTFValues_len;

  795.         for (int t = 0; t < nGroups; t++) {
  796.             int minLen = 32;
  797.             int maxLen = 0;
  798.             final byte[] len_t = len[t];
  799.             for (int i = alphaSize; --i >= 0;) {
  800.                 final int l = len_t[i] & 0xff;
  801.                 if (l > maxLen) {
  802.                     maxLen = l;
  803.                 }
  804.                 if (l < minLen) {
  805.                     minLen = l;
  806.                 }
  807.             }

  808.             // assert (maxLen <= 20) : maxLen;
  809.             // assert (minLen >= 1) : minLen;

  810.             hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
  811.         }
  812.     }

  813.     private void sendMTFValues4() throws IOException {
  814.         final boolean[] inUse = this.data.inUse;
  815.         final boolean[] inUse16 = this.data.sentMTFValues4_inUse16;

  816.         for (int i = 16; --i >= 0;) {
  817.             inUse16[i] = false;
  818.             final int i16 = i * 16;
  819.             for (int j = 16; --j >= 0;) {
  820.                 if (inUse[i16 + j]) {
  821.                     inUse16[i] = true;
  822.                     break;
  823.                 }
  824.             }
  825.         }

  826.         for (int i = 0; i < 16; i++) {
  827.             bsW(1, inUse16[i] ? 1 : 0);
  828.         }

  829.         final OutputStream outShadow = this.out;
  830.         int bsLiveShadow = this.bsLive;
  831.         int bsBuffShadow = this.bsBuff;

  832.         for (int i = 0; i < 16; i++) {
  833.             if (inUse16[i]) {
  834.                 final int i16 = i * 16;
  835.                 for (int j = 0; j < 16; j++) {
  836.                     // inlined: bsW(1, inUse[i16 + j] ? 1 : 0);
  837.                     while (bsLiveShadow >= 8) {
  838.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  839.                         bsBuffShadow <<= 8;
  840.                         bsLiveShadow -= 8;
  841.                     }
  842.                     if (inUse[i16 + j]) {
  843.                         bsBuffShadow |= 1 << 32 - bsLiveShadow - 1;
  844.                     }
  845.                     bsLiveShadow++;
  846.                 }
  847.             }
  848.         }

  849.         this.bsBuff = bsBuffShadow;
  850.         this.bsLive = bsLiveShadow;
  851.     }

  852.     private void sendMTFValues5(final int nGroups, final int nSelectors) throws IOException {
  853.         bsW(3, nGroups);
  854.         bsW(15, nSelectors);

  855.         final OutputStream outShadow = this.out;
  856.         final byte[] selectorMtf = this.data.selectorMtf;

  857.         int bsLiveShadow = this.bsLive;
  858.         int bsBuffShadow = this.bsBuff;

  859.         for (int i = 0; i < nSelectors; i++) {
  860.             for (int j = 0, hj = selectorMtf[i] & 0xff; j < hj; j++) {
  861.                 // inlined: bsW(1, 1);
  862.                 while (bsLiveShadow >= 8) {
  863.                     outShadow.write(bsBuffShadow >> 24);
  864.                     bsBuffShadow <<= 8;
  865.                     bsLiveShadow -= 8;
  866.                 }
  867.                 bsBuffShadow |= 1 << 32 - bsLiveShadow - 1;
  868.                 bsLiveShadow++;
  869.             }

  870.             // inlined: bsW(1, 0);
  871.             while (bsLiveShadow >= 8) {
  872.                 outShadow.write(bsBuffShadow >> 24);
  873.                 bsBuffShadow <<= 8;
  874.                 bsLiveShadow -= 8;
  875.             }
  876.             // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
  877.             bsLiveShadow++;
  878.         }

  879.         this.bsBuff = bsBuffShadow;
  880.         this.bsLive = bsLiveShadow;
  881.     }

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

  885.         int bsLiveShadow = this.bsLive;
  886.         int bsBuffShadow = this.bsBuff;

  887.         for (int t = 0; t < nGroups; t++) {
  888.             final byte[] len_t = len[t];
  889.             int curr = len_t[0] & 0xff;

  890.             // inlined: bsW(5, curr);
  891.             while (bsLiveShadow >= 8) {
  892.                 outShadow.write(bsBuffShadow >> 24); // write 8-bit
  893.                 bsBuffShadow <<= 8;
  894.                 bsLiveShadow -= 8;
  895.             }
  896.             bsBuffShadow |= curr << 32 - bsLiveShadow - 5;
  897.             bsLiveShadow += 5;

  898.             for (int i = 0; i < alphaSize; i++) {
  899.                 final int lti = len_t[i] & 0xff;
  900.                 while (curr < lti) {
  901.                     // inlined: bsW(2, 2);
  902.                     while (bsLiveShadow >= 8) {
  903.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  904.                         bsBuffShadow <<= 8;
  905.                         bsLiveShadow -= 8;
  906.                     }
  907.                     bsBuffShadow |= 2 << 32 - bsLiveShadow - 2;
  908.                     bsLiveShadow += 2;

  909.                     curr++; /* 10 */
  910.                 }

  911.                 while (curr > lti) {
  912.                     // inlined: bsW(2, 3);
  913.                     while (bsLiveShadow >= 8) {
  914.                         outShadow.write(bsBuffShadow >> 24); // write 8-bit
  915.                         bsBuffShadow <<= 8;
  916.                         bsLiveShadow -= 8;
  917.                     }
  918.                     bsBuffShadow |= 3 << 32 - bsLiveShadow - 2;
  919.                     bsLiveShadow += 2;

  920.                     curr--; /* 11 */
  921.                 }

  922.                 // inlined: bsW(1, 0);
  923.                 while (bsLiveShadow >= 8) {
  924.                     outShadow.write(bsBuffShadow >> 24); // write 8-bit
  925.                     bsBuffShadow <<= 8;
  926.                     bsLiveShadow -= 8;
  927.                 }
  928.                 // bsBuffShadow |= 0 << (32 - bsLiveShadow - 1);
  929.                 bsLiveShadow++;
  930.             }
  931.         }

  932.         this.bsBuff = bsBuffShadow;
  933.         this.bsLive = bsLiveShadow;
  934.     }

  935.     private void sendMTFValues7() throws IOException {
  936.         final Data dataShadow = this.data;
  937.         final byte[][] len = dataShadow.sendMTFValues_len;
  938.         final int[][] code = dataShadow.sendMTFValues_code;
  939.         final OutputStream outShadow = this.out;
  940.         final byte[] selector = dataShadow.selector;
  941.         final char[] sfmap = dataShadow.sfmap;
  942.         final int nMTFShadow = this.nMTF;

  943.         int selCtr = 0;

  944.         int bsLiveShadow = this.bsLive;
  945.         int bsBuffShadow = this.bsBuff;

  946.         for (int gs = 0; gs < nMTFShadow;) {
  947.             final int ge = Math.min(gs + G_SIZE - 1, nMTFShadow - 1);
  948.             final int selector_selCtr = selector[selCtr] & 0xff;
  949.             final int[] code_selCtr = code[selector_selCtr];
  950.             final byte[] len_selCtr = len[selector_selCtr];

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

  953.                 //
  954.                 // inlined: bsW(len_selCtr[sfmap_i] & 0xff,
  955.                 // code_selCtr[sfmap_i]);
  956.                 //
  957.                 while (bsLiveShadow >= 8) {
  958.                     outShadow.write(bsBuffShadow >> 24);
  959.                     bsBuffShadow <<= 8;
  960.                     bsLiveShadow -= 8;
  961.                 }
  962.                 final int n = len_selCtr[sfmap_i] & 0xFF;
  963.                 bsBuffShadow |= code_selCtr[sfmap_i] << 32 - bsLiveShadow - n;
  964.                 bsLiveShadow += n;

  965.                 gs++;
  966.             }

  967.             gs = ge + 1;
  968.             selCtr++;
  969.         }

  970.         this.bsBuff = bsBuffShadow;
  971.         this.bsLive = bsLiveShadow;
  972.     }

  973.     @Override
  974.     public void write(final byte[] buf, int offs, final int len) throws IOException {
  975.         if (offs < 0) {
  976.             throw new IndexOutOfBoundsException("offs(" + offs + ") < 0.");
  977.         }
  978.         if (len < 0) {
  979.             throw new IndexOutOfBoundsException("len(" + len + ") < 0.");
  980.         }
  981.         if (offs + len > buf.length) {
  982.             throw new IndexOutOfBoundsException("offs(" + offs + ") + len(" + len + ") > buf.length(" + buf.length + ").");
  983.         }
  984.         checkOpen();
  985.         for (final int hi = offs + len; offs < hi;) {
  986.             write0(buf[offs++]);
  987.         }
  988.     }

  989.     @Override
  990.     public void write(final int b) throws IOException {
  991.         checkOpen();
  992.         write0(b);
  993.     }

  994.     /**
  995.      * Keeps track of the last bytes written and implicitly performs run-length encoding as the first step of the bzip2 algorithm.
  996.      */
  997.     private void write0(int b) throws IOException {
  998.         if (this.currentChar != -1) {
  999.             b &= 0xff;
  1000.             if (this.currentChar == b) {
  1001.                 if (++this.runLength > 254) {
  1002.                     writeRun();
  1003.                     this.currentChar = -1;
  1004.                     this.runLength = 0;
  1005.                 }
  1006.                 // else nothing to do
  1007.             } else {
  1008.                 writeRun();
  1009.                 this.runLength = 1;
  1010.                 this.currentChar = b;
  1011.             }
  1012.         } else {
  1013.             this.currentChar = b & 0xff;
  1014.             this.runLength++;
  1015.         }
  1016.     }

  1017.     /**
  1018.      * 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
  1019.      * identical bytes).
  1020.      *
  1021.      * <p>
  1022.      * Flushes the current block before writing data if it is full.
  1023.      * </p>
  1024.      *
  1025.      * <p>
  1026.      * "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
  1027.      * to point to the last index written minus 1.
  1028.      * </p>
  1029.      */
  1030.     private void writeRun() throws IOException {
  1031.         final int lastShadow = this.last;

  1032.         if (lastShadow < this.allowableBlockSize) {
  1033.             final int currentCharShadow = this.currentChar;
  1034.             final Data dataShadow = this.data;
  1035.             dataShadow.inUse[currentCharShadow] = true;
  1036.             final byte ch = (byte) currentCharShadow;

  1037.             int runLengthShadow = this.runLength;
  1038.             this.crc.update(currentCharShadow, runLengthShadow);

  1039.             switch (runLengthShadow) {
  1040.             case 1:
  1041.                 dataShadow.block[lastShadow + 2] = ch;
  1042.                 this.last = lastShadow + 1;
  1043.                 break;

  1044.             case 2:
  1045.                 dataShadow.block[lastShadow + 2] = ch;
  1046.                 dataShadow.block[lastShadow + 3] = ch;
  1047.                 this.last = lastShadow + 2;
  1048.                 break;

  1049.             case 3: {
  1050.                 final byte[] block = dataShadow.block;
  1051.                 block[lastShadow + 2] = ch;
  1052.                 block[lastShadow + 3] = ch;
  1053.                 block[lastShadow + 4] = ch;
  1054.                 this.last = lastShadow + 3;
  1055.             }
  1056.                 break;

  1057.             default: {
  1058.                 runLengthShadow -= 4;
  1059.                 dataShadow.inUse[runLengthShadow] = true;
  1060.                 final byte[] block = dataShadow.block;
  1061.                 block[lastShadow + 2] = ch;
  1062.                 block[lastShadow + 3] = ch;
  1063.                 block[lastShadow + 4] = ch;
  1064.                 block[lastShadow + 5] = ch;
  1065.                 block[lastShadow + 6] = (byte) runLengthShadow;
  1066.                 this.last = lastShadow + 5;
  1067.             }
  1068.                 break;

  1069.             }
  1070.         } else {
  1071.             endBlock();
  1072.             initBlock();
  1073.             writeRun();
  1074.         }
  1075.     }

  1076. }