Commit bd327e55 authored by Andreas Tille's avatar Andreas Tille

New upstream version 0.9.1

parent d272287d
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>jbzip2</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
#Thu May 19 23:46:53 CST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6
Matthew J. Francis
Copyright (c) 2010 Matthew J. Francis and Contributors of the jbzip2 Project
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
<?xml version="1.0" encoding="MacRoman" standalone="no"?>
<jardesc>
<jar path="jbzip2/jbzip2-0.9.1.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/jbzip2/jbzip2.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
<file path="/jbzip2/AUTHORS"/>
<javaElement handleIdentifier="=jbzip2/src&lt;org.itadaki.bzip2"/>
<javaElement handleIdentifier="=jbzip2/src&lt;demo"/>
<file path="/jbzip2/LICENCE"/>
</selectedElements>
</jardesc>
/*
* Copyright (c) 2011 Matthew Francis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package demo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.itadaki.bzip2.BZip2OutputStream;
/**
* A BZip2 file compressor. For demonstration purposes only
*/
public class Compress {
/**
* @param args
* @throws IOException
*/
public static void main (String[] args) throws IOException {
if (args.length == 0) {
System.err.println ("Demonstration BZip2 compressor\n\nUsage:\n java demo.Compress <filename>\n");
System.exit (1);
}
File inputFile = new File (args[0]);
if (!inputFile.exists() || !inputFile.canRead()) {
System.err.println ("Cannot read file " + inputFile.getPath());
System.exit (1);
}
File outputFile = new File (args[0] + ".bz2");
if (outputFile.exists()) {
System.err.println ("File " + outputFile.getPath() + " already exists");
System.exit (1);
}
InputStream fileInputStream = new BufferedInputStream (new FileInputStream (inputFile));
OutputStream fileOutputStream = new BufferedOutputStream (new FileOutputStream (outputFile), 524288);
BZip2OutputStream outputStream = new BZip2OutputStream (fileOutputStream);
byte[] buffer = new byte [524288];
int bytesRead;
while ((bytesRead = fileInputStream.read (buffer)) != -1) {
outputStream.write (buffer, 0, bytesRead);
}
outputStream.close();
}
}
/*
* Copyright (c) 2011 Matthew Francis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package demo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.itadaki.bzip2.BZip2InputStream;
/**
* A BZip2 file decompressor. For demonstration purposes only
*/
public class Decompress {
/**
* @param args
* @throws IOException
*/
public static void main (String[] args) throws IOException {
if (args.length == 0) {
System.err.println ("Demonstration BZip2 decompressor\n\nUsage:\n java demo.Decompress <filename>\n");
System.exit (1);
}
File inputFile = new File (args[0]);
if (!inputFile.exists() || !inputFile.canRead() || !args[0].endsWith(".bz2")) {
System.err.println ("Cannot read file " + inputFile.getPath());
System.exit (1);
}
File outputFile = new File (args[0].substring (0, args[0].length() - 4));
if (outputFile.exists()) {
System.err.println ("File " + outputFile.getPath() + " already exists");
System.exit (1);
}
InputStream fileInputStream = new BufferedInputStream (new FileInputStream (inputFile));
BZip2InputStream inputStream = new BZip2InputStream (fileInputStream, false);
OutputStream fileOutputStream = new BufferedOutputStream (new FileOutputStream (outputFile), 524288);
byte[] decoded = new byte [524288];
int bytesRead;
while ((bytesRead = inputStream.read (decoded)) != -1) {
fileOutputStream.write (decoded, 0, bytesRead) ;
}
fileOutputStream.close();
}
}
package demo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.itadaki.bzip2.BZip2InputStream;
import org.itadaki.bzip2.BZip2OutputStream;
/**
* Tests the compression/decompression cycle on a file or directory. Compression is performed
* using a temporary file, and the target file or directory is not altered.
*/
public class RoundTrip {
/**
* A FileFilter that returns only ordinary, readable files
*/
private static final FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept (File file) {
return file.isFile() && file.canRead();
}
};
/**
* A FileFilter that returns only readable directories
*/
private static final FileFilter directoryFilter = new FileFilter() {
@Override
public boolean accept (File file) {
return file.isDirectory() && file.canRead();
}
};
/**
* Recursively enumerates ordinary, readable files
* @param base The base file or directory name
* @return A list of ordinary, readable files
*/
private static List<File> getFiles (File base) {
List<File> files = new ArrayList<File>();
if (base.isFile()) {
if (base.canRead()) {
files.add (base);
}
} else {
File[] subDirectories = base.listFiles (directoryFilter);
for (File subDirectory : subDirectories) {
files.addAll (getFiles (subDirectory));
}
for (File file : base.listFiles (fileFilter)) {
files.add (file);
}
}
return files;
}
/**
* Compresses and decompresses each of a list of files, using a temporary file to hold the compressed data
* @param files The files to compress
* @throws IOException On any error compressing or decompressing the files
*/
private static void roundTrip (List<File> files) throws IOException {
File tempFile = File.createTempFile ("rtr", ".tmp");
for (File inputFile : files) {
long inputLength = inputFile.length();
System.out.print (inputLength + " " + inputFile);
// Compress file
InputStream fileInputStream = new BufferedInputStream (new FileInputStream (inputFile));
OutputStream compressedOutputStream = new BufferedOutputStream (new FileOutputStream (tempFile));
BZip2OutputStream bzip2OutputStream = new BZip2OutputStream (compressedOutputStream);
byte[] buffer = new byte [524288];
int bytesRead;
while ((bytesRead = fileInputStream.read (buffer, 0, buffer.length)) != -1) {
bzip2OutputStream.write (buffer, 0, bytesRead);
}
bzip2OutputStream.close();
compressedOutputStream.close();
fileInputStream.close();
long compressedLength = tempFile.length();
System.out.printf(" (%.1f%%)\n", ((float)(inputLength - compressedLength) / inputLength) * 100);
// Decompress file
InputStream compressedInputStream = new BufferedInputStream (new FileInputStream (tempFile));
BZip2InputStream bzip2InputStream = new BZip2InputStream (compressedInputStream, false);
byte[] decoded = new byte [524288];
while ((bytesRead = bzip2InputStream.read (decoded)) != -1);
compressedInputStream.close();
bzip2InputStream.close();
}
}
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.err.println (
"Tests the compression/decompression cycle on a file or directory. Compression is" +
"performed using a temporary file, and the target file or directory is not altered.\n\n" +
"Usage:\n java demo.RoundTrip <file or directory name>\n");
System.exit (1);
}
System.out.println ("Finding files...");
List<File> files = getFiles (new File (args[0]));
System.out.println ("Testing compression/decompression cycle...");
roundTrip(files);
}
}
/*
* Copyright (c) 2011 Matthew Francis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.itadaki.bzip2;
import java.io.IOException;
/*
* Block encoding consists of the following stages:
* 1. Run-Length Encoding[1] - write()
* 2. Burrows Wheeler Transform - close() (through BZip2DivSufSort)
* 3. Write block header - close()
* 4. Move To Front Transform - close() (through BZip2HuffmanStageEncoder)
* 5. Run-Length Encoding[2] - close() (through BZip2HuffmanStageEncoder)
* 6. Create and write Huffman tables - close() (through BZip2HuffmanStageEncoder)
* 7. Huffman encode and write data - close() (through BZip2HuffmanStageEncoder)
*/
/**
* Compresses and writes a single BZip2 block
*/
public class BZip2BlockCompressor {
/**
* The stream to which compressed BZip2 data is written
*/
private final BitOutputStream bitOutputStream;
/**
* CRC builder for the block
*/
private final CRC32 crc = new CRC32();
/**
* The RLE'd block data
*/
private final byte[] block;
/**
* Current length of the RLE'd block data
*/
private int blockLength = 0;
/**
* A limit beyond which new data will not be accepted into the block
*/
private final int blockLengthLimit;
/**
* For each index, {@code true} if that value is present in the block data, otherwise
* {@code false}
*/
private final boolean[] blockValuesPresent = new boolean[256];
/**
* The Burrows Wheeler Transformed block data
*/
private final int[] bwtBlock;
/**
* The current RLE value being accumulated (undefined when {@link #rleLength} is 0)
*/
private int rleCurrentValue = -1;
/**
* The repeat count of the current RLE value
*/
private int rleLength = 0;
/**
* Writes an RLE run to the block array, updating the block CRC and present values array as required
* @param value The value to write
* @param runLength The run length of the value to write
*/
private void writeRun (final int value, int runLength) {
final int blockLength = this.blockLength;
final byte[] block = this.block;
this.blockValuesPresent[value] = true;
this.crc.updateCRC (value, runLength);
final byte byteValue = (byte)value;
switch (runLength) {
case 1:
block[blockLength] = byteValue;
this.blockLength = blockLength + 1;
break;
case 2:
block[blockLength] = byteValue;
block[blockLength + 1] = byteValue;
this.blockLength = blockLength + 2;
break;
case 3:
block[blockLength] = byteValue;
block[blockLength + 1] = byteValue;
block[blockLength + 2] = byteValue;
this.blockLength = blockLength + 3;
break;
default:
runLength -= 4;
this.blockValuesPresent[runLength] = true;
block[blockLength] = byteValue;
block[blockLength + 1] = byteValue;
block[blockLength + 2] = byteValue;
block[blockLength + 3] = byteValue;
block[blockLength + 4] = (byte)runLength;
this.blockLength = blockLength + 5;
break;
}
}
/**
* Writes a byte to the block, accumulating to an RLE run where possible
* @param value The byte to write
* @return {@code true} if the byte was written, or {@code false} if the block is already full
*/
public boolean write (final int value) {
if (this.blockLength > this.blockLengthLimit) {
return false;
}
final int rleCurrentValue = this.rleCurrentValue;
final int rleLength = this.rleLength;
if (rleLength == 0) {
this.rleCurrentValue = value;
this.rleLength = 1;
} else if (rleCurrentValue != value) {
// This path commits us to write 6 bytes - one RLE run (5 bytes) plus one extra
writeRun (rleCurrentValue & 0xff, rleLength);
this.rleCurrentValue = value;
this.rleLength = 1;
} else {
if (rleLength == 254) {
writeRun (rleCurrentValue & 0xff, 255);
this.rleLength = 0;
} else {
this.rleLength = rleLength + 1;
}
}
return true;
}
/**
* Writes an array to the block
* @param data The array to write
* @param offset The offset within the input data to write from
* @param length The number of bytes of input data to write
* @return The actual number of input bytes written. May be less than the number requested, or
* zero if the block is already full
*/
public int write (final byte[] data, int offset, int length) {
int written = 0;
while (length-- > 0) {
if (!write (data[offset++])) {
break;
}
written++;
}
return written;
}
/**
* Compresses and writes out the block
* @throws IOException on any I/O error writing the data
*/
public void close() throws IOException {
// If an RLE run is in progress, write it out
if (this.rleLength > 0) {
writeRun (this.rleCurrentValue & 0xff, this.rleLength);
}
// Apply a one byte block wrap required by the BWT implementation
this.block[this.blockLength] = this.block[0];
// Perform the Burrows Wheeler Transform
BZip2DivSufSort divSufSort = new BZip2DivSufSort (this.block, this.bwtBlock, this.blockLength);
int bwtStartPointer = divSufSort.bwt();
// Write out the block header
this.bitOutputStream.writeBits (24, BZip2Constants.BLOCK_HEADER_MARKER_1);
this.bitOutputStream.writeBits (24, BZip2Constants.BLOCK_HEADER_MARKER_2);
this.bitOutputStream.writeInteger (this.crc.getCRC());
this.bitOutputStream.writeBoolean (false); // Randomised block flag. We never create randomised blocks
this.bitOutputStream.writeBits (24, bwtStartPointer);
// Perform the Huffman Encoding stage and write out the encoded data
BZip2HuffmanStageEncoder huffmanEncoder = new BZip2HuffmanStageEncoder (this.bitOutputStream, this.blockValuesPresent, this.bwtBlock, this.blockLength);
huffmanEncoder.encode();
}
/**
* Determines if any bytes have been written to the block
* @return {@code true} if one or more bytes has been written to the block, otherwise
* {@code false}
*/
public boolean isEmpty() {
return ((this.blockLength == 0) && (this.rleLength == 0));
}
/**
* Gets the CRC of the completed block. Only valid after calling {@link #close()}
* @return The block's CRC
*/
public int getCRC() {
return this.crc.getCRC();
}
/**
* @param bitOutputStream The stream to which compressed BZip2 data is written
* @param blockSize The declared block size in bytes. Up to this many bytes will be accepted
* into the block after Run-Length Encoding is applied
*/
public BZip2BlockCompressor (final BitOutputStream bitOutputStream, final int blockSize) {
this.bitOutputStream = bitOutputStream;
// One extra byte is added to allow for the block wrap applied in close()
this.block = new byte[blockSize + 1];
this.bwtBlock = new int[blockSize + 1];
this.blockLengthLimit = blockSize - 6; // 5 bytes for one RLE run plus one byte - see {@link #write(int)}
}
}
This diff is collapsed.
/*
* Copyright (c) 2011 Matthew Francis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.itadaki.bzip2;
/**
* BZip2 constants shared between the compressor and decompressor
*/
interface BZip2Constants {
/**
* First three bytes of the block header marker
*/
static final int BLOCK_HEADER_MARKER_1 = 0x314159;
/**
* Last three bytes of the block header marker
*/
static final int BLOCK_HEADER_MARKER_2 = 0x265359;
/**
* Number of symbols decoded after which a new Huffman table is selected
*/
static final int HUFFMAN_GROUP_RUN_LENGTH = 50;
/**
* Maximum possible Huffman alphabet size
*/
static final int HUFFMAN_MAXIMUM_ALPHABET_SIZE = 258;
/**
* The longest Huffman code length created by the encoder
*/
static final int HUFFMAN_ENCODE_MAXIMUM_CODE_LENGTH = 20;
/**
* The longest Huffman code length accepted by the decoder
*/
static final int HUFFMAN_DECODE_MAXIMUM_CODE_LENGTH = 23;
/**
* Maximum number of alternative Huffman tables
*/
static final int HUFFMAN_MAXIMUM_TABLES = 6;