Cleanup of Bzip2 compressor files

2.x-1.16
szymon 2020-08-02 20:29:15 +02:00
parent be3c78e44b
commit a1a745abbb
26 changed files with 22 additions and 1284 deletions

View File

@ -58,7 +58,7 @@ public class ParallelZipCompressor {
} catch (IOException | InterruptedException | ExecutionException e) { } catch (IOException | InterruptedException | ExecutionException e) {
TextileBackup.LOGGER.error("An exception happened!", e); TextileBackup.LOGGER.error("An exception happened!", e);
Utilities.sendError("Something went wrong while compressing files!", ctx);; Utilities.sendError("Something went wrong while compressing files!", ctx);
} }
long end = System.nanoTime(); long end = System.nanoTime();

View File

@ -65,7 +65,7 @@ final class BZip2EncoderExecutorServiceImpl implements BZip2EncoderExecutorServi
BZip2EncoderExecutorServiceImpl(int noThreads, ErrorState es) BZip2EncoderExecutorServiceImpl(int noThreads, ErrorState es)
{ {
m_executor = new ThreadPoolExecutor(noThreads, noThreads, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1), new EncodingThreadFactory(es), ShoehornInJobRejectedExecutionHandler.INSTANCE); m_executor = new ThreadPoolExecutor(noThreads, noThreads, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new EncodingThreadFactory(es), ShoehornInJobRejectedExecutionHandler.INSTANCE);
m_errorState = es; m_errorState = es;
} }

View File

@ -159,9 +159,8 @@ public class BZip2OutputStream extends OutputStream
private void writeEosBlock() throws IOException private void writeEosBlock() throws IOException
{ {
// Write the end of stream magic // Write the end of stream magic
for (int i = 0; i < EOS_MAGIC.length; i++) for (byte b : EOS_MAGIC) {
{ m_wrapped.writeBitsLittleEndian(b & 0xFF, 8);
m_wrapped.writeBitsLittleEndian(EOS_MAGIC[i] & 0xFF, 8);
} }
// Write file checksum // Write file checksum
m_wrapped.writeBitsLittleEndian(m_blockOutputStream.getFileChecksum(), 32); m_wrapped.writeBitsLittleEndian(m_blockOutputStream.getFileChecksum(), 32);
@ -264,16 +263,6 @@ public class BZip2OutputStream extends OutputStream
return this == o; return this == o;
} }
/**
* Close the stream if the client has been sloppy about it.
*/
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
/** /**
* Create a {@link BZip2EncoderExecutorService} that can be shared between * Create a {@link BZip2EncoderExecutorService} that can be shared between
* several {@link BZip2OutputStream}:s to spread the bzip2 encoding work * several {@link BZip2OutputStream}:s to spread the bzip2 encoding work

View File

@ -1,29 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
/**
* Interface identifying a bzip2 data block. Used by the {@link BlockDecoder}.
* @author Karl Gustafsson
* @since 1.1
*/
interface Block
{
// Nothing
}

View File

@ -1,422 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
import java.io.IOException;
import java.util.Arrays;
import org.at4j.support.comp.ByteMoveToFront;
import org.at4j.support.comp.IntMoveToFront;
import org.at4j.support.io.LittleEndianBitInputStream;
import org.at4j.support.lang.At4JException;
import org.at4j.support.lang.UnsignedInteger;
/**
* This is used by the {@link BZip2InputStream} to decode data blocks.
* @author Karl Gustafsson
* @since 1.1
*/
final class BlockDecoder
{
// The magic number identifying a block of compressed data
private static final byte[] COMPRESSED_BLOCK_MAGIC = new byte[] { (byte) 0x31, (byte) 0x41, (byte) 0x59, (byte) 0x26, (byte) 0x53, (byte) 0x59 };
// The magic number identifying the end of stream block
private static final byte[] EOS_BLOCK_MAGIC = new byte[] { (byte) 0x17, (byte) 0x72, (byte) 0x45, (byte) 0x38, (byte) 0x50, (byte) 0x90 };
// The number of symbols to read from each Huffman tree before switching
private static final int SYMBOLS_TO_READ_FROM_EACH_TREE = 50;
// The symbol value of the special RUNA symbol.
private static final int RUNA_SYMBOL = 0;
// The symbol value of the special RUNB symbol.
private static final int RUNB_SYMBOL = 1;
private static final int MAX_NO_OF_MTF_SYMBOLS = 258;
private static final byte[] INITIAL_MOVE_TO_FRONT_ALPHABET = new byte[MAX_NO_OF_MTF_SYMBOLS];
static
{
for (int i = 0; i < MAX_NO_OF_MTF_SYMBOLS; i++)
{
INITIAL_MOVE_TO_FRONT_ALPHABET[i] = (byte) i;
}
}
private final LittleEndianBitInputStream m_in;
private final int m_blockSize;
// Data read from the block header
// Block checksum (CRC)
private int m_readBlockChecksum;
// The pointer to the original data used in the BW transform
private int m_originalDataPointer;
// The Huffman trees used for decompression
private HighValueBranchHuffmanTree[] m_huffmanTrees;
// The EOB (End Of Block) symbol index.
private int m_endOfBlockSymbol;
// The number of times that the Huffman trees are switched in the input.
// The trees are switched every 50 bytes.
private int m_numberOfTimesHuffmanTreesAreSwitched;
private int[] m_treeUse;
// Mapping between symbol values and byte values.
private byte[] m_symbolSequenceNos;
// Frequency of each byte in the pre-BW data
private int[] m_byteFrequencies;
// State variables
// The number of the currently selected Huffman tree
private HighValueBranchHuffmanTree m_curTree;
// The number of symbols left to read from the current Huffman tree
private int m_symbolsLeftToReadFromCurTree;
// The current number of Huffman tree switches
private int m_switchNo;
// A counter for the number of bytes decoded in this block.
private int m_noBytesDecoded;
private ByteMoveToFront m_mtfTransformer;
// This will hold the decoded data (before the Burrows Wheeler decoding)
private final byte[] m_decoded;
BlockDecoder(LittleEndianBitInputStream in, int blockSize)
{
m_in = in;
m_blockSize = blockSize;
m_decoded = new byte[blockSize];
}
private void throwIOException(String msg) throws IOException
{
throw new IOException(msg + ". Position in input stream: " + m_in.getNumberOfBytesRead());
}
private void checkInterrupted() throws InterruptedException
{
if (Thread.interrupted())
{
throw new InterruptedException();
}
}
private void trace(String s)
{
System.out.println(s);
}
static HighValueBranchHuffmanTree decodeHuffmanTree(final int totalNumberOfSymbols, final LittleEndianBitInputStream in) throws IOException
{
int[] symbolLengths = new int[totalNumberOfSymbols];
// Starting bit length for Huffman deltas in this tree
int currentBitLength = in.readBits(5);
if (currentBitLength > 20)
{
throw new IOException("Invalid starting bit length for Huffman deltas: " + currentBitLength + ". Must be <= 20");
}
// Initialize min and max lengths per tree with values that
// will certainly be overwritten.
int minBitLengthPerTree = 20;
int maxBitLengthPerTree = 0;
for (int j = 0; j < totalNumberOfSymbols; j++)
{
while (in.readBit())
{
currentBitLength += in.readBit() ? -1 : 1;
if ((currentBitLength < 1) || (currentBitLength > 20))
{
throw new IOException("Invalid bit length " + currentBitLength);
}
}
symbolLengths[j] = currentBitLength;
if (currentBitLength < minBitLengthPerTree)
{
minBitLengthPerTree = currentBitLength;
}
if (currentBitLength > maxBitLengthPerTree)
{
maxBitLengthPerTree = currentBitLength;
}
}
return new HighValueBranchHuffmanTree(symbolLengths, minBitLengthPerTree, maxBitLengthPerTree, false);
}
private void readCompressedBlockHeader() throws IOException
{
byte[] barr = new byte[4];
// Block checksum
m_readBlockChecksum = (int) UnsignedInteger.fromLittleEndianByteArrayToLong(m_in.readBytes(barr, 0, 4), 0);
// Randomized block?
if (m_in.readBit())
{
throwIOException("Randomized block mode is not supported");
}
// Starting pointer into BWT
m_in.readBytes(barr, 1, 3);
barr[0] = 0;
m_originalDataPointer = (int) UnsignedInteger.fromLittleEndianByteArrayToLong(barr, 0);
if (m_originalDataPointer > m_blockSize)
{
throw new IOException("Invalid starting pointer " + m_originalDataPointer + ". It must be less than the block size " + m_blockSize);
}
// Huffman used codes
boolean[] usedSymbols = new boolean[256];
int numberOfUsedSymbols = 0;
boolean[] inUseBlocks = new boolean[16];
for (int i = 0; i < 16; i++)
{
inUseBlocks[i] = m_in.readBit();
}
for (int i = 0; i < 16; i++)
{
if (inUseBlocks[i])
{
for (int j = 0; j < 16; j++)
{
if (m_in.readBit())
{
usedSymbols[i * 16 + j] = true;
numberOfUsedSymbols++;
}
}
}
}
if (numberOfUsedSymbols == 0)
{
throwIOException("No symbols used in table");
}
// Create a mapping for the sequence numbers of all used bytes
m_symbolSequenceNos = new byte[numberOfUsedSymbols];
int useIndex = 0;
for (int i = 0; i < 256; i++)
{
if (usedSymbols[i])
{
m_symbolSequenceNos[useIndex++] = (byte) (i & 0xFF);
}
}
assert useIndex == numberOfUsedSymbols;
m_byteFrequencies = new int[256];
// The number of Huffman trees to use
int numberOfHuffmanTrees = m_in.readBits(3);
if (numberOfHuffmanTrees < 2 || numberOfHuffmanTrees > 6)
{
throwIOException("Invalid number of Huffman trees " + numberOfHuffmanTrees + ". Must be between 2 and 6 (inclusive)");
}
// The number of times the trees to use are swapped in the input.
// The trees are swapped each 50 bytes.
m_numberOfTimesHuffmanTreesAreSwitched = m_in.readBitsLittleEndian(15);
if (m_numberOfTimesHuffmanTreesAreSwitched < 1)
{
throwIOException("Invalid number of times the Huffman trees are switched in the input: " + m_numberOfTimesHuffmanTreesAreSwitched);
}
// Zero-terminated bit runs for each tree switch
int[] treeUseMtf = new int[m_numberOfTimesHuffmanTreesAreSwitched];
for (int i = 0; i < m_numberOfTimesHuffmanTreesAreSwitched; i++)
{
treeUseMtf[i] = 0;
while (m_in.readBit())
{
treeUseMtf[i]++;
}
if (treeUseMtf[i] > numberOfHuffmanTrees)
{
throwIOException("Invalid Huffman tree use MTF " + treeUseMtf[i] + ". Must be less than the number of Huffman trees, " + numberOfHuffmanTrees);
}
}
// Decode the tree use MTF values
m_treeUse = new int[m_numberOfTimesHuffmanTreesAreSwitched];
// The "alphabet" for the MTF encoding -- the indices of the different
// tree uses.
int[] treeUseIndices = new int[numberOfHuffmanTrees];
for (int i = 0; i < numberOfHuffmanTrees; i++)
{
treeUseIndices[i] = i;
}
new IntMoveToFront(treeUseIndices).decode(treeUseMtf, m_treeUse);
// Settings for the Huffman trees
// The total number of used symbols is the value we calculated above - 1
// + RUNA, RUNB and an end of stream marker.
int totalNumberOfSymbols = numberOfUsedSymbols + 2;
m_huffmanTrees = new HighValueBranchHuffmanTree[numberOfHuffmanTrees];
for (int i = 0; i < numberOfHuffmanTrees; i++)
{
m_huffmanTrees[i] = decodeHuffmanTree(totalNumberOfSymbols, m_in);
}
// The symbol value for the end of the data block.
m_endOfBlockSymbol = totalNumberOfSymbols - 1;
}
private void selectNewHuffmanTree() throws IOException
{
if (m_switchNo >= m_numberOfTimesHuffmanTreesAreSwitched)
{
throwIOException("One Huffman tree switch too many: " + m_switchNo);
}
m_symbolsLeftToReadFromCurTree = SYMBOLS_TO_READ_FROM_EACH_TREE;
m_curTree = m_huffmanTrees[m_treeUse[m_switchNo]];
m_switchNo++;
}
private int readSymbol() throws IOException
{
if (m_symbolsLeftToReadFromCurTree == 0)
{
selectNewHuffmanTree();
}
final int symbol = m_curTree.readNext(m_in);
m_symbolsLeftToReadFromCurTree--;
return symbol;
}
private void decodeSingleByte(final int symbolMtf) throws IOException
{
// Move To Front decode the symbol
final int byteIndex = m_mtfTransformer.decode(symbolMtf - 1) & 0xFF;
final byte value = m_symbolSequenceNos[byteIndex];
m_decoded[m_noBytesDecoded++] = value;
m_byteFrequencies[value & 0xFF]++;
}
// returns the next symbol
private int handleRunaAndRunb(int symbol) throws IOException
{
int n = 1;
int multiplier = 0;
while (symbol == RUNA_SYMBOL || symbol == RUNB_SYMBOL)
{
if (symbol == RUNA_SYMBOL)
{
multiplier += n;
}
else
{
multiplier += 2 * n;
}
// Multiply n with 2
n <<= 1;
symbol = readSymbol();
}
// The repeated value is at the front of the MTF list
final int byteIndex = m_mtfTransformer.decode(0) & 0xFF;
final byte value = m_symbolSequenceNos[byteIndex];
if (multiplier == 1)
{
m_decoded[m_noBytesDecoded++] = value;
m_byteFrequencies[value & 0xFF]++;
}
else
{
Arrays.fill(m_decoded, m_noBytesDecoded, m_noBytesDecoded + multiplier, value);
m_noBytesDecoded += multiplier;
m_byteFrequencies[value & 0xFF] += multiplier;
}
return symbol;
}
CompressedDataBlock readCompressedDataBlock() throws IOException, InterruptedException
{
readCompressedBlockHeader();
int symbol = readSymbol();
while (true)
{
checkInterrupted();
if (symbol == RUNA_SYMBOL || symbol == RUNB_SYMBOL)
{
symbol = handleRunaAndRunb(symbol);
}
else if (symbol == m_endOfBlockSymbol)
{
BurrowsWheelerDecoder bwd = new BurrowsWheelerDecoder(m_decoded, m_noBytesDecoded, m_byteFrequencies, m_originalDataPointer);
return new CompressedDataBlock(new RLEDecodingInputStream(bwd.decode(), m_readBlockChecksum), m_readBlockChecksum);
}
else
{
decodeSingleByte(symbol);
symbol = readSymbol();
}
}
}
private void initDecoderState()
{
// Initialize the MTF alphabet
final byte[] moveToFrontAlphabet = new byte[MAX_NO_OF_MTF_SYMBOLS];
System.arraycopy(INITIAL_MOVE_TO_FRONT_ALPHABET, 0, moveToFrontAlphabet, 0, MAX_NO_OF_MTF_SYMBOLS);
m_mtfTransformer = new ByteMoveToFront(moveToFrontAlphabet);
m_curTree = null;
m_symbolsLeftToReadFromCurTree = 0;
m_switchNo = 0;
m_noBytesDecoded = 0;
}
Block getNextBlock() throws IOException
{
initDecoderState();
byte[] barr = new byte[6];
m_in.readBytes(barr, 0, 6);
if (Arrays.equals(COMPRESSED_BLOCK_MAGIC, barr))
{
trace("Found block of compressed data");
try
{
return readCompressedDataBlock();
}
catch (InterruptedException e)
{
throw new At4JException(e);
}
}
else if (Arrays.equals(EOS_BLOCK_MAGIC, barr))
{
trace("Found end of stream block");
m_in.readBytes(barr, 0, 4);
int readCrc32 = (int) UnsignedInteger.fromLittleEndianByteArrayToLong(barr, 0);
return new EosBlock(readCrc32);
}
else
{
throwIOException("Invalid block header " + Arrays.toString(barr) + ". Expected compressed data block or end of stream block");
// Never reached
return null;
}
}
}

View File

@ -179,7 +179,7 @@ final class BlockEncoder
{ {
byte[] res = m_scratchpad.m_sequenceMap; byte[] res = m_scratchpad.m_sequenceMap;
byte index = 0; byte index = 0;
for (int i = 0; i < symbols.length; i++) for (int i : symbols)
{ {
res[symbols[i] & 0xFF] = index++; res[symbols[i] & 0xFF] = index++;
} }
@ -754,9 +754,8 @@ final class BlockEncoder
private void writeBlockHeader(final int blockChecksum, int bwFirstPointer, boolean[] seenDifferentBytes, MTFAndRLEResult mtfrle, HuffmanTreesAndUsage htau) throws IOException private void writeBlockHeader(final int blockChecksum, int bwFirstPointer, boolean[] seenDifferentBytes, MTFAndRLEResult mtfrle, HuffmanTreesAndUsage htau) throws IOException
{ {
// Block magic // Block magic
for (int i = 0; i < BLOCK_MAGIC.length; i++) for (int b : BLOCK_MAGIC) {
{ m_out.writeBitsLittleEndian(b & 0xFF, 8);
m_out.writeBitsLittleEndian(BLOCK_MAGIC[i] & 0xFF, 8);
} }
// Checksum // Checksum
m_out.writeBitsLittleEndian(blockChecksum, 32); m_out.writeBitsLittleEndian(blockChecksum, 32);

View File

@ -44,16 +44,7 @@ final class BlockEncoderRunnable implements Runnable
m_encoder.setScratchpad(((EncodingThread) Thread.currentThread()).getScratchpad()); m_encoder.setScratchpad(((EncodingThread) Thread.currentThread()).getScratchpad());
m_encoder.encode(); m_encoder.encode();
} }
catch (IOException e) catch (IOException | Error | RuntimeException e)
{
((EncodingThread) Thread.currentThread()).getErrorState().registerError(e, m_errorOwner);
}
catch (RuntimeException e)
{
((EncodingThread) Thread.currentThread()).getErrorState().registerError(e, m_errorOwner);
}
catch (Error e)
{ {
((EncodingThread) Thread.currentThread()).getErrorState().registerError(e, m_errorOwner); ((EncodingThread) Thread.currentThread()).getErrorState().registerError(e, m_errorOwner);

View File

@ -307,9 +307,8 @@ final class BlockOutputStream extends OutputStream
@Override @Override
public void write(final byte[] data) throws IOException public void write(final byte[] data) throws IOException
{ {
for (int i = 0; i < data.length; i++) for (int datum : data) {
{ write(datum & 0xFF);
write(data[i] & 0xFF);
} }
} }

View File

@ -1,120 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
import java.io.IOException;
import java.io.InputStream;
/**
* Decode Burrows Wheeler encoded data.
* @author Karl Gustafsson
* @since 1.1
*/
final class BurrowsWheelerDecoder
{
static class BWInputStream extends InputStream
{
private final byte[] m_decoded;
private final int[] m_ptr;
private int m_curPointer;
private boolean m_eof;
private int m_noLeftToRead;
BWInputStream(byte[] decoded, int[] ptr, int originalDataPointer)
{
m_decoded = decoded;
m_ptr = ptr;
m_curPointer = ptr[originalDataPointer];
m_noLeftToRead = ptr.length;
}
@Override
public int read() throws IOException
{
if (m_eof)
{
return -1;
}
final int res = m_decoded[m_curPointer] & 0xFF;
m_eof = --m_noLeftToRead == 0;
m_curPointer = m_ptr[m_curPointer];
return res;
}
}
private final byte[] m_decoded;
private final int m_noBytesDecoded;
private final int[] m_byteFrequencies;
private final int m_originalDataPointer;
/**
* @param encoded The encoded data. This array may be longer than the actual
* amount of encoded data. The {@code noBytesDecoded} parameter determines
* how much of the array that will be used.
* @param noBytesEncoded The length of the encoded data.
* @param byteFrequencies The number of times each byte occur in the data.
* @param originalDataPointer The row number of the original data in the
* Burrows Wheeler matrix.
* @throws IOException On I/O errors.
*/
BurrowsWheelerDecoder(byte[] encoded, int noBytesEncoded, int[] byteFrequencies, int originalDataPointer) throws IOException
{
if (originalDataPointer > noBytesEncoded)
{
throw new IOException("Invalid pointer to original data in block header " + originalDataPointer + ". It is larger than the size of data in the block " + noBytesEncoded);
}
m_decoded = encoded;
m_noBytesDecoded = noBytesEncoded;
m_byteFrequencies = byteFrequencies;
m_originalDataPointer = originalDataPointer;
}
InputStream decode()
{
// Calculate the transformation vector used to move from the encoded
// data to the decoded.
// The byte frequency array contains the frequency of each byte in the
// data. Create a new array tarr that, for each byte, specifies how many
// bytes of lower value that occurs in the data.
int[] tarr = new int[256];
tarr[0] = 0;
for (int i = 1; i < 256; i++)
{
tarr[i] = tarr[i - 1] + m_byteFrequencies[i - 1];
}
// The ptr array will contain a chain of positions of the decoded bytes
// in the decoded array.
final int[] ptr = new int[m_noBytesDecoded];
for (int i = 0; i < m_noBytesDecoded; i++)
{
int val = m_decoded[i] & 0xFF;
// Get the position of the decoded byte position in tt. Increment
// the tt position for the given value so that next occurrence of the
// value will end up in the next position in tt.
int ttPos = tarr[val]++;
ptr[ttPos] = i;
}
return new BWInputStream(m_decoded, ptr, m_originalDataPointer);
}
}

View File

@ -1,51 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
import java.io.InputStream;
/**
* A bzip2 block containing compressed data.
* @author Karl Gustafsson
* @since 1.1
*/
final class CompressedDataBlock implements Block
{
private final InputStream m_stream;
private final int m_blockChecksum;
CompressedDataBlock(InputStream stream, int blockChecksum)
{
// Null check
stream.getClass();
m_stream = stream;
m_blockChecksum = blockChecksum;
}
InputStream getStream()
{
return m_stream;
}
int getBlockChecksum()
{
return m_blockChecksum;
}
}

View File

@ -35,7 +35,7 @@ final class EncodedBlockWriter
{ {
// All variables are protected by this object's intrinsic lock // All variables are protected by this object's intrinsic lock
private final BitOutput m_out; private final BitOutput m_out;
private final Map<Integer, EncodedBlockData> m_savedBlocks = new HashMap<Integer, EncodedBlockData>(); private final Map<Integer, EncodedBlockData> m_savedBlocks = new HashMap<>();
// This latch is used to signal to the bzip2 output stream when this writer // This latch is used to signal to the bzip2 output stream when this writer
// is finished. // is finished.
private final CountDownLatch m_doneLatch = new CountDownLatch(1); private final CountDownLatch m_doneLatch = new CountDownLatch(1);
@ -119,19 +119,7 @@ final class EncodedBlockWriter
saveBlock(blockNo, blockData); saveBlock(blockNo, blockData);
} }
} }
catch (Error e) catch (Error | RuntimeException | IOException e)
{
m_hasError = true;
m_doneLatch.countDown();
throw e;
}
catch (RuntimeException e)
{
m_hasError = true;
m_doneLatch.countDown();
throw e;
}
catch (IOException e)
{ {
m_hasError = true; m_hasError = true;
m_doneLatch.countDown(); m_doneLatch.countDown();

View File

@ -1,39 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
/**
* A bzip2 block containing end of stream information.
* @author Karl Gustafsson
* @since 1.1
*/
final class EosBlock implements Block
{
private final long m_readCrc;
EosBlock(long readCrc)
{
m_readCrc = readCrc;
}
long getReadCrc()
{
return m_readCrc;
}
}

View File

@ -231,7 +231,7 @@ final class HighValueBranchHuffmanTree
final int d2 = w2 & 0xFF; final int d2 = w2 & 0xFF;
final int ww1 = w1 & 0xFFFFFF00; final int ww1 = w1 & 0xFFFFFF00;
final int ww2 = w2 & 0xFFFFFF00; final int ww2 = w2 & 0xFFFFFF00;
return (ww1 + ww2) | (1 + (d1 > d2 ? d1 : d2)); return (ww1 + ww2) | (1 + (Math.max(d1, d2)));
} }
int getMinLength() int getMinLength()

View File

@ -34,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
final class MultipleObserverErrorState implements ErrorState final class MultipleObserverErrorState implements ErrorState
{ {
private Map<Object, Throwable> m_errors = new ConcurrentHashMap<Object, Throwable>(4); private Map<Object, Throwable> m_errors = new ConcurrentHashMap<>(4);
public void checkAndClearErrors(Object ownerToken) throws Error, RuntimeException, IOException public void checkAndClearErrors(Object ownerToken) throws Error, RuntimeException, IOException
{ {

View File

@ -1,164 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.comp.bzip2;
import java.io.IOException;
import java.io.InputStream;
/**
* This stream run length decodes read data. It is used by the
* {@link BZip2InputStream}.
* @author Karl Gustafsson
* @since 1.1
*/
final class RLEDecodingInputStream extends InputStream
{
private static enum RLEState
{
READING, REPEATING, ABOUT_TO_READ_HOW_MANY_TO_REPEAT, EOF;
}
// Block checksum calculated while reading the block contents.
private final CRC m_blockChecksum = new CRC();
private final InputStream m_wrapped;
private final long m_readChecksum;
private RLEState m_state;
private int m_noLeftToRepeat;
private int m_last;
private int m_numberOfSimilar;
RLEDecodingInputStream(InputStream wrapped, long readChecksum)
{
m_wrapped = wrapped;
m_readChecksum = readChecksum;
m_state = RLEState.READING;
m_numberOfSimilar = 0;
m_last = -1;
}
private void handleEof() throws IOException
{
if (m_blockChecksum.getValue() != m_readChecksum)
{
throw new IOException("Invalid block checksum. Was " + m_blockChecksum.getValue() + ", expected " + m_readChecksum);
}
}
@Override
public int read() throws IOException
{
switch (m_state)
{
case EOF:
return -1;
case READING:
int val = m_wrapped.read();
if (val == -1)
{
m_state = RLEState.EOF;
handleEof();
return -1;
}
if (val == m_last)
{
m_numberOfSimilar++;
if (m_numberOfSimilar == 4)
{
// Four in a row. The next value is a repeat number.
m_state = RLEState.ABOUT_TO_READ_HOW_MANY_TO_REPEAT;
m_numberOfSimilar = 0;
}
}
else
{
m_numberOfSimilar = 1;
m_last = val;
}
m_blockChecksum.update(val);
return val;
case ABOUT_TO_READ_HOW_MANY_TO_REPEAT:
m_noLeftToRepeat = m_wrapped.read();
if (m_noLeftToRepeat == -1)
{
// A rather unexpected EOF
m_state = RLEState.EOF;
handleEof();
return -1;
}
else if (m_noLeftToRepeat == 0)
{
// Nothing to repeat. Go on to read the next value.
m_state = RLEState.READING;
return read();
}
else
{
m_state = RLEState.REPEATING;
m_noLeftToRepeat--;
if (m_noLeftToRepeat == 0)
{
// Just one to repeat, which we will do in this call.
m_state = RLEState.READING;
}
m_blockChecksum.update(m_last);
return m_last;
}
case REPEATING:
m_noLeftToRepeat--;
if (m_noLeftToRepeat == 0)
{
m_state = RLEState.READING;
}
m_blockChecksum.update(m_last);
return m_last;
default:
throw new RuntimeException("Unknown state " + m_state + ". This is a bug");
}
}
@Override
public int read(byte[] barr, int off, int len) throws IOException
{
// The ranges are validated by BZip2InputStream
for (int i = 0; i < len; i++)
{
int b = read();
if (b < 0)
{
// EOF
return i > 0 ? i : -1;
}
barr[off + i] = (byte) (b & 0xFF);
}
return len;
}
@Override
public void close() throws IOException
{
m_wrapped.close();
super.close();
}
}

View File

@ -30,7 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
*/ */
final class SingleObserverErrorState implements ErrorState final class SingleObserverErrorState implements ErrorState
{ {
private final AtomicReference<Throwable> m_exception = new AtomicReference<Throwable>(); private final AtomicReference<Throwable> m_exception = new AtomicReference<>();
public void checkAndClearErrors(Object ownerToken) throws Error, RuntimeException, IOException public void checkAndClearErrors(Object ownerToken) throws Error, RuntimeException, IOException
{ {

View File

@ -606,13 +606,7 @@ final class ThreeWayRadixQuicksort
*/ */
private void addRangeToStack(final int bucketStartPos, final int bucketLen, final int depth) private void addRangeToStack(final int bucketStartPos, final int bucketLen, final int depth)
{ {
if (bucketLen < 2) if(bucketLen >= 2) {
{
// Already sorted
return;
}
else
{
m_sortStack[++m_sortStackPointer] = new QuickSortRangeInfo(bucketStartPos, bucketLen, depth); m_sortStack[++m_sortStackPointer] = new QuickSortRangeInfo(bucketStartPos, bucketLen, depth);
} }
} }

View File

@ -138,10 +138,7 @@ public class ByteMoveToFront
public byte decode(int index) public byte decode(int index)
{ {
byte val = m_alphabet[index]; byte val = m_alphabet[index];
for (int j = index; j > 0; j--) System.arraycopy(m_alphabet, 0, m_alphabet, 1, index);
{
m_alphabet[j] = m_alphabet[j - 1];
}
m_alphabet[0] = val; m_alphabet[0] = val;
return val; return val;
} }

View File

@ -135,10 +135,7 @@ public class IntMoveToFront
public int decode(int index) public int decode(int index)
{ {
int val = m_alphabet[index]; int val = m_alphabet[index];
for (int j = index; j > 0; j--) System.arraycopy(m_alphabet, 0, m_alphabet, 1, index);
{
m_alphabet[j] = m_alphabet[j - 1];
}
m_alphabet[0] = val; m_alphabet[0] = val;
return val; return val;
} }
@ -165,10 +162,7 @@ public class IntMoveToFront
{ {
int index = in[i]; int index = in[i];
int val = m_alphabet[index]; int val = m_alphabet[index];
for (int j = index; j > 0; j--) System.arraycopy(m_alphabet, 0, m_alphabet, 1, index);
{
m_alphabet[j] = m_alphabet[j - 1];
}
m_alphabet[0] = val; m_alphabet[0] = val;
out[i] = val; out[i] = val;
} }

View File

@ -1,380 +0,0 @@
/* AT4J -- Archive file tools for Java -- http://www.at4j.org
* Copyright (C) 2009 Karl Gustafsson
*
* This file is a part of AT4J
*
* AT4J is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* AT4J is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.at4j.support.io;
import java.io.IOException;
import java.io.InputStream;
/**
* This is an input stream that a client can use to read single or several bits
* from an underlying {@link InputStream}. The bits are read in little-endian
* bit order.
* @author Karl Gustafsson
* @since 1.1
*/
public class LittleEndianBitInputStream extends InputStream implements BitInput
{
// 2^0
private static final int POINTER_START_OF_BYTE = 0;
// 2^7
private static final int POINTER_END_OF_BYTE = 7;
private final InputStream m_in;
// The current byte
private int m_curByte;
// The pointer to the current bit location in the current byte.
private int m_pointerInByte = POINTER_START_OF_BYTE;
private long m_numberOfBytesRead = 0;
public LittleEndianBitInputStream(InputStream in) throws IOException
{
// Null check
in.getClass();
m_in = in;
m_curByte = in.read();
// Don't increment the number of read bytes counter. It is always one
// byte behind.
}
private int readByte() throws IOException
{
int res = m_in.read();
m_numberOfBytesRead += res != -1 ? 1 : 0;
return res;
}
private void incrementPointerPosition() throws IOException
{
if (m_pointerInByte == POINTER_END_OF_BYTE)
{
// Read a new byte
m_curByte = readByte();
m_pointerInByte = POINTER_START_OF_BYTE;
}
else
{
// Increment the pointer only if we're not at EOF
if (!isAtEof())
{
m_pointerInByte++;
}
}
}
public boolean isAtEof()
{
return m_curByte == -1;
}
/**
* Get the number of whole bytes read this far.
* @return The number of bytes read this far.
*/
public long getNumberOfBytesRead()
{
return m_numberOfBytesRead;
}
private void assertNotAtEOF() throws IOException
{
if (isAtEof())
{
throwIOException("At EOF");
}
}
private boolean isAtByteBoundary()
{
return m_pointerInByte == POINTER_START_OF_BYTE;
}
private void assertAtByteBoundary() throws IOException
{
if (!isAtByteBoundary())
{
throwIOException("Not at byte boundary. Position: pos=" + m_pointerInByte);
}
}
private void throwIOException(String msg, long pos) throws IOException
{
throw new IOException(msg + ". Position in stream: " + pos);
}
private void throwIOException(String msg) throws IOException
{
throw new IOException(msg + ". Position in stream: " + m_numberOfBytesRead);
}
public void skipToByteBoundary() throws IOException
{
assertNotAtEOF();
if (m_pointerInByte != POINTER_START_OF_BYTE)
{
m_pointerInByte = POINTER_START_OF_BYTE;
m_curByte = readByte();
}
}
public boolean readBit() throws IOException
{
assertNotAtEOF();
boolean res = (m_curByte & (1 << (7 - m_pointerInByte))) > 0;
incrementPointerPosition();
return res;
}
public int readBits(int no) throws IOException, IndexOutOfBoundsException
{
if (no < 0 || no > 8)
{
throw new IndexOutOfBoundsException("Invalid number of bits: " + no + ". Must be between 0 and 8 (inclusive)");
}
assertNotAtEOF();
if (no == 0)
{
return 0;
}
// Bytes are stored little bit endian
if (no + m_pointerInByte <= 8)
{
// All bits to read fit in the current byte
int res = (m_curByte >> (8 - no - m_pointerInByte)) & ((1 << no) - 1);
m_pointerInByte += no;
if (m_pointerInByte > POINTER_END_OF_BYTE)
{
m_curByte = readByte();
m_pointerInByte = POINTER_START_OF_BYTE;
}
return res;
}
else
{
// Read remaining bits + first bits of next byte
int noToReadInByte2 = no - (8 - m_pointerInByte);
int res = (m_curByte & ((1 << (8 - m_pointerInByte)) - 1)) << noToReadInByte2;
m_curByte = readByte();
assertNotAtEOF();
m_pointerInByte = noToReadInByte2;
res += m_curByte >> (8 - noToReadInByte2);
return res;
}
}
public int readBitsLittleEndian(int no) throws IOException, IndexOutOfBoundsException
{
if (no < 0 || no > 32)
{
throw new IndexOutOfBoundsException("Invalid number of bits: " + no + ". Must be between 0 and 32 (inclusive)");
}
if (no == 0)
{
return 0;
}
int noReads = no / 8;
int mod = no % 8;
int res = 0;
if (mod != 0)
{
res = readBits(mod) << (noReads * 8);
}
for (int i = 0; i < noReads; i++)
{
res += readBits(8) << ((noReads - i - 1) * 8);
}
return res;
}
public byte[] readBytes(byte[] barr, int off, int len) throws IOException, IndexOutOfBoundsException
{
if (off < 0)
{
throw new IndexOutOfBoundsException("Invalid offset " + off + ". It must be >= 0");
}
if (len < 0)
{
throw new IndexOutOfBoundsException("Invalid length " + len + ". It must be >= 0");
}
if (off + len > barr.length)
{
throw new IndexOutOfBoundsException("Invalid offset + length (" + off + " + " + len + "). It must be <= the length of the supplied array (" + barr.length + ")");
}
assertNotAtEOF();
if (len == 0)
{
return barr;
}
if (isAtByteBoundary())
{
// Special case: we are at the byte boundary. We just have to read
// the len next bytes and return them.
// The read method takes care of updating all internal state.
int noRead = read(barr, off, len);
if (noRead != len)
{
throwIOException("Unexpected EOF. Wanted to read " + len + " bytes. Got " + noRead, m_numberOfBytesRead - noRead);
}
}
else
{
int noRead = m_in.read(barr, off, len);
m_numberOfBytesRead += noRead;
if (noRead != len)
{
m_curByte = -1;
m_pointerInByte = POINTER_START_OF_BYTE;
throwIOException("Unexpected EOF. Wanted to read " + len + " bytes. Got " + noRead, m_numberOfBytesRead - noRead);
}
// Shift bytes in the result array. Bytes are stored little (bit-)
// endian.
int lastByte = m_curByte;
m_curByte = barr[off + len - 1] & 0xFF;
// The distance to shift the second byte to the right.
int rightShiftDistance = 8 - m_pointerInByte;
for (int i = off; i < off + len; i++)
{
int newLastByte = barr[i];
barr[i] = (byte) (((lastByte << m_pointerInByte) | ((barr[i] & 0xFF) >>> rightShiftDistance)) & 0xFF);
lastByte = newLastByte;
}
}
return barr;
}
@Override
public int read() throws IOException
{
assertAtByteBoundary();
int res = m_curByte;
if (m_curByte != -1)
{
m_curByte = readByte();
}
return res;
}
@Override
public int read(byte[] barr) throws IOException
{
return read(barr, 0, barr.length);
}
@Override
public int read(byte[] barr, int offset, int len) throws IndexOutOfBoundsException, IOException
{
if (offset < 0)
{
throw new IndexOutOfBoundsException("Illegal offset: " + offset);
}
else if (len < 0)
{
throw new IndexOutOfBoundsException("Illegal length: " + len);
}
else if ((offset + len) > barr.length)
{
throw new IndexOutOfBoundsException("Illegal offset + length: " + offset + " + " + len + ". Longer than the byte array: " + barr.length);
}
assertAtByteBoundary();
if (isAtEof())
{
return -1;
}
else
{
barr[offset] = (byte) m_curByte;
int res = 1;
if (len > 1)
{
int noRead = m_in.read(barr, offset + 1, len - 1);
if (noRead > 0)
{
res += noRead;
m_numberOfBytesRead += noRead;
}
}
m_curByte = readByte();
return res;
}
}
@Override
public long skip(long n) throws IOException
{
assertAtByteBoundary();
if (n <= 0L)
{
return 0L;
}
else
{
if (isAtEof())
{
return 0L;
}
if (n > 1L)
{
long noToSkip = n - 1L;
long noSkipped = m_in.skip(noToSkip);
m_numberOfBytesRead += noSkipped;
if (noSkipped < noToSkip)
{
// At EOF
m_curByte = -1;
return noSkipped + 1;
}
else
{
m_curByte = readByte();
return noSkipped + 1;
}
}
else
{
m_curByte = readByte();
return 1L;
}
}
}
@Override
public int available() throws IOException
{
assertAtByteBoundary();
return m_in.available() + m_curByte != -1 ? 1 : 0;
}
@Override
public void close() throws IOException
{
m_in.close();
}
}

View File

@ -186,7 +186,7 @@ public class SignedInteger implements Comparable<SignedInteger>
public int compareTo(SignedInteger l2) public int compareTo(SignedInteger l2)
{ {
return Integer.valueOf(m_value).compareTo(Integer.valueOf(l2.m_value)); return Integer.valueOf(m_value).compareTo(l2.m_value);
} }
@Override @Override

View File

@ -202,7 +202,7 @@ public class SignedLong implements Comparable<SignedLong>
public int compareTo(SignedLong l2) public int compareTo(SignedLong l2)
{ {
return Long.valueOf(m_value).compareTo(Long.valueOf(l2.m_value)); return Long.valueOf(m_value).compareTo(l2.m_value);
} }
@Override @Override

View File

@ -18,8 +18,6 @@
*/ */
package org.at4j.support.lang; package org.at4j.support.lang;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;

View File

@ -18,8 +18,6 @@
*/ */
package org.at4j.support.lang; package org.at4j.support.lang;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
/** /**
@ -232,7 +230,7 @@ public final class UnsignedInteger implements Serializable, Comparable<UnsignedI
public int compareTo(UnsignedInteger i2) public int compareTo(UnsignedInteger i2)
{ {
return Long.valueOf(longValue()).compareTo(Long.valueOf(i2.longValue())); return Long.valueOf(longValue()).compareTo(i2.longValue());
} }
@Override @Override

View File

@ -18,8 +18,6 @@
*/ */
package org.at4j.support.lang; package org.at4j.support.lang;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigInteger; import java.math.BigInteger;

View File

@ -18,8 +18,6 @@
*/ */
package org.at4j.support.lang; package org.at4j.support.lang;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable; import java.io.Serializable;