diff --git a/build.xml b/build.xml index fca2271d2e..3414fa49f7 100755 --- a/build.xml +++ b/build.xml @@ -348,6 +348,8 @@ + + @@ -372,6 +374,8 @@ + + diff --git a/lib/jni/libIntelDeflater.so b/lib/jni/libIntelDeflater.so new file mode 100755 index 0000000000..2f25735ef5 Binary files /dev/null and b/lib/jni/libIntelDeflater.so differ diff --git a/src/c/inteldeflater/IntelDeflater.c b/src/c/inteldeflater/IntelDeflater.c new file mode 100644 index 0000000000..39a3a57977 --- /dev/null +++ b/src/c/inteldeflater/IntelDeflater.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Native method support for net.sf.samtools.util.zip.IntelDeflater. + * This is copied from OpenJDK native support for java.util.zip.Deflater, with only package and class name changed. + */ + +#include +#include +#include "jlong.h" +#include "jni.h" +#include "jni_util.h" +#include "zlib.h" + +#include "net_sf_samtools_util_zip_IntelDeflater.h" + +#define DEF_MEM_LEVEL 8 + +static jfieldID levelID; +static jfieldID strategyID; +static jfieldID setParamsID; +static jfieldID finishID; +static jfieldID finishedID; +static jfieldID bufID, offID, lenID; + +JNIEXPORT void JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_initIDs(JNIEnv *env, jclass cls) +{ + levelID = (*env)->GetFieldID(env, cls, "level", "I"); + strategyID = (*env)->GetFieldID(env, cls, "strategy", "I"); + setParamsID = (*env)->GetFieldID(env, cls, "setParams", "Z"); + finishID = (*env)->GetFieldID(env, cls, "finish", "Z"); + finishedID = (*env)->GetFieldID(env, cls, "finished", "Z"); + bufID = (*env)->GetFieldID(env, cls, "buf", "[B"); + offID = (*env)->GetFieldID(env, cls, "off", "I"); + lenID = (*env)->GetFieldID(env, cls, "len", "I"); +} + +JNIEXPORT jlong JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_init(JNIEnv *env, jclass cls, jint level, + jint strategy, jboolean nowrap) +{ + z_stream *strm = calloc(1, sizeof(z_stream)); + + if (strm == 0) { + JNU_ThrowOutOfMemoryError(env, 0); + return jlong_zero; + } else { + char *msg; + switch (deflateInit2(strm, level, Z_DEFLATED, + nowrap ? -MAX_WBITS : MAX_WBITS, + DEF_MEM_LEVEL, strategy)) { + case Z_OK: + return ptr_to_jlong(strm); + case Z_MEM_ERROR: + free(strm); + JNU_ThrowOutOfMemoryError(env, 0); + return jlong_zero; + case Z_STREAM_ERROR: + free(strm); + JNU_ThrowIllegalArgumentException(env, 0); + return jlong_zero; + default: + msg = strm->msg; + free(strm); + JNU_ThrowInternalError(env, msg); + return jlong_zero; + } + } +} + +JNIEXPORT void JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_setDictionary(JNIEnv *env, jclass cls, jlong addr, + jarray b, jint off, jint len) +{ + Bytef *buf = (*env)->GetPrimitiveArrayCritical(env, b, 0); + int res; + if (buf == 0) {/* out of memory */ + return; + } + res = deflateSetDictionary((z_stream *)jlong_to_ptr(addr), buf + off, len); + (*env)->ReleasePrimitiveArrayCritical(env, b, buf, 0); + switch (res) { + case Z_OK: + break; + case Z_STREAM_ERROR: + JNU_ThrowIllegalArgumentException(env, 0); + break; + default: + JNU_ThrowInternalError(env, ((z_stream *)jlong_to_ptr(addr))->msg); + break; + } +} + +JNIEXPORT jint JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_deflateBytes(JNIEnv *env, jobject this, jlong addr, + jarray b, jint off, jint len, jint flush) +{ + z_stream *strm = jlong_to_ptr(addr); + + jarray this_buf = (*env)->GetObjectField(env, this, bufID); + jint this_off = (*env)->GetIntField(env, this, offID); + jint this_len = (*env)->GetIntField(env, this, lenID); + jbyte *in_buf; + jbyte *out_buf; + int res; + if ((*env)->GetBooleanField(env, this, setParamsID)) { + int level = (*env)->GetIntField(env, this, levelID); + int strategy = (*env)->GetIntField(env, this, strategyID); + in_buf = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0); + if (in_buf == NULL) { + // Throw OOME only when length is not zero + if (this_len != 0) + JNU_ThrowOutOfMemoryError(env, 0); + return 0; + } + out_buf = (*env)->GetPrimitiveArrayCritical(env, b, 0); + if (out_buf == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); + if (len != 0) + JNU_ThrowOutOfMemoryError(env, 0); + return 0; + } + + strm->next_in = (Bytef *) (in_buf + this_off); + strm->next_out = (Bytef *) (out_buf + off); + strm->avail_in = this_len; + strm->avail_out = len; + res = deflateParams(strm, level, strategy); + (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0); + (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); + + switch (res) { + case Z_OK: + (*env)->SetBooleanField(env, this, setParamsID, JNI_FALSE); + this_off += this_len - strm->avail_in; + (*env)->SetIntField(env, this, offID, this_off); + (*env)->SetIntField(env, this, lenID, strm->avail_in); + return len - strm->avail_out; + case Z_BUF_ERROR: + (*env)->SetBooleanField(env, this, setParamsID, JNI_FALSE); + return 0; + default: + JNU_ThrowInternalError(env, strm->msg); + return 0; + } + } else { + jboolean finish = (*env)->GetBooleanField(env, this, finishID); + in_buf = (*env)->GetPrimitiveArrayCritical(env, this_buf, 0); + if (in_buf == NULL) { + if (this_len != 0) + JNU_ThrowOutOfMemoryError(env, 0); + return 0; + } + out_buf = (*env)->GetPrimitiveArrayCritical(env, b, 0); + if (out_buf == NULL) { + (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); + if (len != 0) + JNU_ThrowOutOfMemoryError(env, 0); + + return 0; + } + + strm->next_in = (Bytef *) (in_buf + this_off); + strm->next_out = (Bytef *) (out_buf + off); + strm->avail_in = this_len; + strm->avail_out = len; + res = deflate(strm, finish ? Z_FINISH : flush); + (*env)->ReleasePrimitiveArrayCritical(env, b, out_buf, 0); + (*env)->ReleasePrimitiveArrayCritical(env, this_buf, in_buf, 0); + + switch (res) { + case Z_STREAM_END: + (*env)->SetBooleanField(env, this, finishedID, JNI_TRUE); + /* fall through */ + case Z_OK: + this_off += this_len - strm->avail_in; + (*env)->SetIntField(env, this, offID, this_off); + (*env)->SetIntField(env, this, lenID, strm->avail_in); + return len - strm->avail_out; + case Z_BUF_ERROR: + return 0; + default: + JNU_ThrowInternalError(env, strm->msg); + return 0; + } + } +} + +JNIEXPORT jint JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_getAdler(JNIEnv *env, jclass cls, jlong addr) +{ + return ((z_stream *)jlong_to_ptr(addr))->adler; +} + +JNIEXPORT jlong JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_getBytesRead(JNIEnv *env, jclass cls, jlong addr) +{ + return ((z_stream *)jlong_to_ptr(addr))->total_in; +} + +JNIEXPORT jlong JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_getBytesWritten(JNIEnv *env, jclass cls, jlong addr) +{ + return ((z_stream *)jlong_to_ptr(addr))->total_out; +} + +JNIEXPORT void JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_reset(JNIEnv *env, jclass cls, jlong addr) +{ + if (deflateReset((z_stream *)jlong_to_ptr(addr)) != Z_OK) { + JNU_ThrowInternalError(env, 0); + } +} + +JNIEXPORT void JNICALL +Java_net_sf_samtools_util_zip_IntelDeflater_end(JNIEnv *env, jclass cls, jlong addr) +{ + if (deflateEnd((z_stream *)jlong_to_ptr(addr)) == Z_STREAM_ERROR) { + JNU_ThrowInternalError(env, 0); + } else { + free((z_stream *)jlong_to_ptr(addr)); + } +} diff --git a/src/java/net/sf/picard/cmdline/CommandLineProgram.java b/src/java/net/sf/picard/cmdline/CommandLineProgram.java index 9134dfcc4c..ac65a59a70 100644 --- a/src/java/net/sf/picard/cmdline/CommandLineProgram.java +++ b/src/java/net/sf/picard/cmdline/CommandLineProgram.java @@ -40,6 +40,7 @@ import net.sf.samtools.util.BlockCompressedOutputStream; import net.sf.samtools.util.BlockCompressedStreamConstants; import net.sf.samtools.util.IOUtil; +import net.sf.samtools.util.zip.DeflaterFactory; /** * Abstract class to facilitate writing command-line programs. @@ -167,7 +168,8 @@ public int instanceMain(final String[] argv) { " on " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch") + "; " + System.getProperty("java.vm.name") + " " + System.getProperty("java.runtime.version") + - "; Picard version: " + commandLineParser.getVersion()); + "; Picard version: " + commandLineParser.getVersion() + + " " + (DeflaterFactory.usingIntelDeflater()? "IntelDeflater": "JdkDeflater")); } catch (Exception e) { /* Unpossible! */ } } diff --git a/src/java/net/sf/samtools/Defaults.java b/src/java/net/sf/samtools/Defaults.java index 4c3a652094..9f395008b4 100644 --- a/src/java/net/sf/samtools/Defaults.java +++ b/src/java/net/sf/samtools/Defaults.java @@ -22,12 +22,21 @@ public class Defaults { /** Buffer size, in bytes, used whenever reading/writing files or streams. Default = 128k. */ public static final int BUFFER_SIZE; + /** Should BlockCompressedOutputStream attempt to load libIntelDeflater? */ + public static final boolean TRY_USE_INTEL_DEFLATER; + + /** Path to libIntelDeflater.so. If this is not set, the library is looked for in the directory + * where the executable jar lives. */ + public static final String INTEL_DEFLATER_SHARED_LIBRARY_PATH; + static { CREATE_INDEX = getBooleanProperty("create_index", false); CREATE_MD5 = getBooleanProperty("create_md5", false); USE_ASYNC_IO = getBooleanProperty("use_async_io", false); COMPRESSION_LEVEL = getIntProperty("compression_level", 5); BUFFER_SIZE = getIntProperty("buffer_size", 1024 * 128); + TRY_USE_INTEL_DEFLATER = getBooleanProperty(";", true); + INTEL_DEFLATER_SHARED_LIBRARY_PATH = getStringProperty("intel_deflater_so_path", null); } /** Gets a string system property, prefixed with "samjdk." using the default if the property does not exist.*/ diff --git a/src/java/net/sf/samtools/util/BlockCompressedOutputStream.java b/src/java/net/sf/samtools/util/BlockCompressedOutputStream.java index 5b453e6e10..ac1834c82b 100644 --- a/src/java/net/sf/samtools/util/BlockCompressedOutputStream.java +++ b/src/java/net/sf/samtools/util/BlockCompressedOutputStream.java @@ -23,6 +23,8 @@ */ package net.sf.samtools.util; +import net.sf.samtools.util.zip.DeflaterFactory; + import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -83,6 +85,8 @@ public static int getDefaultCompressionLevel() { // which would attempt to compress up to 64K bytes, and if the resulting compressed block was too large, // try compressing fewer input bytes (aka "downshifting'). The problem with downshifting is that // getFilePointer might return an inaccurate value. + // I assume (AW 29-Oct-2013) that there is no value in using hardware-assisted deflater for no-compression mode, + // so just use JDK standard. private final Deflater noCompressionDeflater = new Deflater(Deflater.NO_COMPRESSION, true); private final CRC32 crc32 = new CRC32(); private File file = null; @@ -121,7 +125,7 @@ public BlockCompressedOutputStream(final String filename, final int compressionL public BlockCompressedOutputStream(final File file, final int compressionLevel) { this.file = file; codec = new BinaryCodec(file, true); - deflater = new Deflater(compressionLevel, true); + deflater = DeflaterFactory.makeDeflater(compressionLevel, true); } /** @@ -138,7 +142,7 @@ public BlockCompressedOutputStream(final OutputStream os, final File file, final if (file != null) { codec.setOutputFileName(file.getAbsolutePath()); } - deflater = new Deflater(compressionLevel, true); + deflater = DeflaterFactory.makeDeflater(compressionLevel, true); } /** diff --git a/src/java/net/sf/samtools/util/zip/DeflaterFactory.java b/src/java/net/sf/samtools/util/zip/DeflaterFactory.java new file mode 100644 index 0000000000..0c6af8c78b --- /dev/null +++ b/src/java/net/sf/samtools/util/zip/DeflaterFactory.java @@ -0,0 +1,78 @@ +/* + * The MIT License + * + * Copyright (c) 2013 The Broad Institute + * + * 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 net.sf.samtools.util.zip; + +import net.sf.samtools.Defaults; +import net.sf.samtools.SAMException; + +import java.lang.reflect.Constructor; +import java.util.zip.Deflater; + +/** + * Create zlib-based Deflater if JNI library and other require libraries are available, otherwise create standard + * JDK Deflater. + * Java 7 has its own Deflater implementation (libzip.so). This is almost as fast as a zlib-based Deflater, so in general + * there isn't a compelling reason to use zlib. However, Intel has created a hardware-assisted zlib implementation + * as part of their IPP (Integrated Performance Primitives) package that can run significantly faster on some Intel + * hardware. We have seen compression times reduced by 13% to 33% depending on particular hardware, and hope that + * newer Intel processors will be even better. + * + * Note that this class will no longer be necessary once Java 8 is required, because JDK 8 will use zlib instead + * of libzip implementation. + */ +public class DeflaterFactory { + + private static Constructor intelDeflaterConstructor; + + static { + try { + if (Defaults.TRY_USE_INTEL_DEFLATER) { + final Class clazz = (Class) Class.forName("net.sf.samtools.util.zip.IntelDeflater"); + intelDeflaterConstructor = clazz.getConstructor(Integer.TYPE, Boolean.TYPE); + } + } catch (ClassNotFoundException e) { + intelDeflaterConstructor = null; + } catch (NoSuchMethodException e) { + intelDeflaterConstructor = null; + } catch (UnsatisfiedLinkError e) { + intelDeflaterConstructor = null; + } + } + + public static Deflater makeDeflater(final int compressionLevel, final boolean nowrap) { + if (intelDeflaterConstructor != null) { + try { + return intelDeflaterConstructor.newInstance(compressionLevel, nowrap); + } catch (Exception e) { + throw new SAMException("Exception constructing IntelDeflater", e); + } + } else { + return new Deflater(compressionLevel, nowrap); + } + } + + public static boolean usingIntelDeflater() { + return intelDeflaterConstructor != null; + } +} diff --git a/src/java/net/sf/samtools/util/zip/IntelDeflater.java b/src/java/net/sf/samtools/util/zip/IntelDeflater.java new file mode 100644 index 0000000000..1ce5de6d65 --- /dev/null +++ b/src/java/net/sf/samtools/util/zip/IntelDeflater.java @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package net.sf.samtools.util.zip; + +import net.sf.samtools.Defaults; + +import java.io.File; +import java.net.URL; +import java.util.zip.Deflater; + +/** + * This is a copy of java.util.zip.Deflater from OpenJDK 7, with the following changes: + * - package and class name changed + * - static block to load libIntelDeflater library + * - extends java.util.zip.Deflater so that IntelDeflater object can be used as regular Deflater object. + * Note however that all methods of Deflater are overridden. + * + * The shared library is found via one of the following mechanisms: + * 1. if samjdk.intel_deflater_so_path system property is set, this is assumed to be the path of libIntelDeflater.so + * 2. If system property is not set, directory where the jarfile that this class came from lives is tried as location + * of libIntelDeflater.so + * 3. If either of the above fails to find the library, regular LD_LIBRARY_PATH is used to find the library. + * 4. If that doesn't work, class fails to load and code falls back to regular Java Deflater class. + * + * + * The rest of this document is copied verbatim from the original OpenJDK file. + * + * This class provides support for general purpose compression using the + * popular ZLIB compression library. The ZLIB compression library was + * initially developed as part of the PNG graphics standard and is not + * protected by patents. It is fully described in the specifications at + * the java.util.zip + * package description. + * + *

The following code fragment demonstrates a trivial compression + * and decompression of a string using IntelDeflater and + * Inflater. + * + *

+ * try {
+ *     // Encode a String into bytes
+ *     String inputString = "blahblahblah";
+ *     byte[] input = inputString.getBytes("UTF-8");
+ *
+ *     // Compress the bytes
+ *     byte[] output = new byte[100];
+ *     IntelDeflater compresser = new IntelDeflater();
+ *     compresser.setInput(input);
+ *     compresser.finish();
+ *     int compressedDataLength = compresser.deflate(output);
+ *     compresser.end();
+ *
+ *     // Decompress the bytes
+ *     Inflater decompresser = new Inflater();
+ *     decompresser.setInput(output, 0, compressedDataLength);
+ *     byte[] result = new byte[100];
+ *     int resultLength = decompresser.inflate(result);
+ *     decompresser.end();
+ *
+ *     // Decode the bytes into a String
+ *     String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch(java.io.UnsupportedEncodingException ex) {
+ *     // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ *     // handle
+ * }
+ * 
+ * + * @see java.util.zip.Inflater + * @author David Connelly + */ +public +class IntelDeflater extends Deflater { + + private final ZStreamRef zsRef; + private byte[] buf = new byte[0]; + private int off, len; + private int level, strategy; + private boolean setParams; + private boolean finish, finished; + + /** + * Compression flush mode used to achieve best compression result. + * + * @see IntelDeflater#deflate(byte[], int, int, int) + * @since 1.7 + */ + public static final int NO_FLUSH = 0; + + /** + * Compression flush mode used to flush out all pending output; may + * degrade compression for some compression algorithms. + * + * @see IntelDeflater#deflate(byte[], int, int, int) + * @since 1.7 + */ + public static final int SYNC_FLUSH = 2; + + /** + * Compression flush mode used to flush out all pending output and + * reset the deflater. Using this mode too often can seriously degrade + * compression. + * + * @see IntelDeflater#deflate(byte[], int, int, int) + * @since 1.7 + */ + public static final int FULL_FLUSH = 3; + + static { + try { + final File sharedLibrary; + if (Defaults.INTEL_DEFLATER_SHARED_LIBRARY_PATH != null) { + // Load via path set by -Dsamjdk.intel_deflater_so_path= + sharedLibrary = new File(Defaults.INTEL_DEFLATER_SHARED_LIBRARY_PATH); + } else { + // Look in directory containing this class for the library + URL jarUrl = IntelDeflater.class.getProtectionDomain().getCodeSource().getLocation(); + sharedLibrary = new File(new File(jarUrl.getPath()).getParentFile(), "libIntelDeflater.so"); + } + System.load(sharedLibrary.getAbsolutePath()); + } catch (Throwable e) { + // Possible exceptions: + // System.load: UnsatisfiedLinkError + // getProtectionDomain: SecurityException + // NullPointerException due to getCodeSource returning null + + // Try to find via LD_LIBRARY_PATH + System.loadLibrary("IntelDeflater"); + } + initIDs(); + } + + /** + * Creates a new compressor using the specified compression level. + * If 'nowrap' is true then the ZLIB header and checksum fields will + * not be used in order to support the compression format used in + * both GZIP and PKZIP. + * @param level the compression level (0-9) + * @param nowrap if true then use GZIP compatible compression + */ + public IntelDeflater(int level, boolean nowrap) { + this.level = level; + this.strategy = DEFAULT_STRATEGY; + this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap)); + } + + /** + * Creates a new compressor using the specified compression level. + * Compressed data will be generated in ZLIB format. + * @param level the compression level (0-9) + */ + public IntelDeflater(int level) { + this(level, false); + } + + /** + * Creates a new compressor with the default compression level. + * Compressed data will be generated in ZLIB format. + */ + public IntelDeflater() { + this(DEFAULT_COMPRESSION, false); + } + + /** + * Sets input data for compression. This should be called whenever + * needsInput() returns true indicating that more input data is required. + * @param b the input data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see IntelDeflater#needsInput + */ + @Override + public void setInput(byte[] b, int off, int len) { + if (b== null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + this.buf = b; + this.off = off; + this.len = len; + } + } + + /** + * Sets input data for compression. This should be called whenever + * needsInput() returns true indicating that more input data is required. + * @param b the input data bytes + * @see IntelDeflater#needsInput + */ + @Override + public void setInput(byte[] b) { + setInput(b, 0, b.length); + } + + /** + * Sets preset dictionary for compression. A preset dictionary is used + * when the history buffer can be predetermined. When the data is later + * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called + * in order to get the Adler-32 value of the dictionary required for + * decompression. + * @param b the dictionary data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see java.util.zip.Inflater#inflate + * @see java.util.zip.Inflater#getAdler + */ + @Override + public void setDictionary(byte[] b, int off, int len) { + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + ensureOpen(); + setDictionary(zsRef.address(), b, off, len); + } + } + + /** + * Sets preset dictionary for compression. A preset dictionary is used + * when the history buffer can be predetermined. When the data is later + * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called + * in order to get the Adler-32 value of the dictionary required for + * decompression. + * @param b the dictionary data bytes + * @see java.util.zip.Inflater#inflate + * @see java.util.zip.Inflater#getAdler + */ + @Override + public void setDictionary(byte[] b) { + setDictionary(b, 0, b.length); + } + + /** + * Sets the compression strategy to the specified value. + * @param strategy the new compression strategy + * @exception IllegalArgumentException if the compression strategy is + * invalid + */ + @Override + public void setStrategy(int strategy) { + switch (strategy) { + case DEFAULT_STRATEGY: + case FILTERED: + case HUFFMAN_ONLY: + break; + default: + throw new IllegalArgumentException(); + } + synchronized (zsRef) { + if (this.strategy != strategy) { + this.strategy = strategy; + setParams = true; + } + } + } + + /** + * Sets the current compression level to the specified value. + * @param level the new compression level (0-9) + * @exception IllegalArgumentException if the compression level is invalid + */ + @Override + public void setLevel(int level) { + if ((level < 0 || level > 9) && level != DEFAULT_COMPRESSION) { + throw new IllegalArgumentException("invalid compression level"); + } + synchronized (zsRef) { + if (this.level != level) { + this.level = level; + setParams = true; + } + } + } + + /** + * Returns true if the input data buffer is empty and setInput() + * should be called in order to provide more input. + * @return true if the input data buffer is empty and setInput() + * should be called in order to provide more input + */ + @Override + public boolean needsInput() { + return len <= 0; + } + + /** + * When called, indicates that compression should end with the current + * contents of the input buffer. + */ + @Override + public void finish() { + synchronized (zsRef) { + finish = true; + } + } + + /** + * Returns true if the end of the compressed data output stream has + * been reached. + * @return true if the end of the compressed data output stream has + * been reached + */ + @Override + public boolean finished() { + synchronized (zsRef) { + return finished; + } + } + + /** + * Compresses the input data and fills specified buffer with compressed + * data. Returns actual number of bytes of compressed data. A return value + * of 0 indicates that {@link #needsInput() needsInput} should be called + * in order to determine if more input data is required. + * + *

This method uses {@link #NO_FLUSH} as its compression flush mode. + * An invocation of this method of the form {@code deflater.deflate(b, off, len)} + * yields the same result as the invocation of + * {@code deflater.deflate(b, off, len, IntelDeflater.NO_FLUSH)}. + * + * @param b the buffer for the compressed data + * @param off the start offset of the data + * @param len the maximum number of bytes of compressed data + * @return the actual number of bytes of compressed data written to the + * output buffer + */ + @Override + public int deflate(byte[] b, int off, int len) { + return deflate(b, off, len, NO_FLUSH); + } + + /** + * Compresses the input data and fills specified buffer with compressed + * data. Returns actual number of bytes of compressed data. A return value + * of 0 indicates that {@link #needsInput() needsInput} should be called + * in order to determine if more input data is required. + * + *

This method uses {@link #NO_FLUSH} as its compression flush mode. + * An invocation of this method of the form {@code deflater.deflate(b)} + * yields the same result as the invocation of + * {@code deflater.deflate(b, 0, b.length, IntelDeflater.NO_FLUSH)}. + * + * @param b the buffer for the compressed data + * @return the actual number of bytes of compressed data written to the + * output buffer + */ + @Override + public int deflate(byte[] b) { + return deflate(b, 0, b.length, NO_FLUSH); + } + + /** + * Compresses the input data and fills the specified buffer with compressed + * data. Returns actual number of bytes of data compressed. + * + *

Compression flush mode is one of the following three modes: + * + *

    + *
  • {@link #NO_FLUSH}: allows the deflater to decide how much data + * to accumulate, before producing output, in order to achieve the best + * compression (should be used in normal use scenario). A return value + * of 0 in this flush mode indicates that {@link #needsInput()} should + * be called in order to determine if more input data is required. + * + *
  • {@link #SYNC_FLUSH}: all pending output in the deflater is flushed, + * to the specified output buffer, so that an inflater that works on + * compressed data can get all input data available so far (In particular + * the {@link #needsInput()} returns {@code true} after this invocation + * if enough output space is provided). Flushing with {@link #SYNC_FLUSH} + * may degrade compression for some compression algorithms and so it + * should be used only when necessary. + * + *
  • {@link #FULL_FLUSH}: all pending output is flushed out as with + * {@link #SYNC_FLUSH}. The compression state is reset so that the inflater + * that works on the compressed output data can restart from this point + * if previous compressed data has been damaged or if random access is + * desired. Using {@link #FULL_FLUSH} too often can seriously degrade + * compression. + *
+ * + *

In the case of {@link #FULL_FLUSH} or {@link #SYNC_FLUSH}, if + * the return value is {@code len}, the space available in output + * buffer {@code b}, this method should be invoked again with the same + * {@code flush} parameter and more output space. + * + * @param b the buffer for the compressed data + * @param off the start offset of the data + * @param len the maximum number of bytes of compressed data + * @param flush the compression flush mode + * @return the actual number of bytes of compressed data written to + * the output buffer + * + * @throws IllegalArgumentException if the flush mode is invalid + * @since 1.7 + */ + public int deflate(byte[] b, int off, int len, int flush) { + //System.out.println("Inside IntelDeflater\n"); + if (b == null) { + throw new NullPointerException(); + } + if (off < 0 || len < 0 || off > b.length - len) { + throw new ArrayIndexOutOfBoundsException(); + } + synchronized (zsRef) { + ensureOpen(); + if (flush == NO_FLUSH || flush == SYNC_FLUSH || + flush == FULL_FLUSH) + return deflateBytes(zsRef.address(), b, off, len, flush); + throw new IllegalArgumentException(); + } + } + + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + @Override + public int getAdler() { + synchronized (zsRef) { + ensureOpen(); + return getAdler(zsRef.address()); + } + } + + /** + * Returns the total number of uncompressed bytes input so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of uncompressed bytes input so far + */ + @Override + public int getTotalIn() { + return (int) getBytesRead(); + } + + /** + * Returns the total number of uncompressed bytes input so far.

+ * + * @return the total (non-negative) number of uncompressed bytes input so far + * @since 1.5 + */ + @Override + public long getBytesRead() { + synchronized (zsRef) { + ensureOpen(); + return getBytesRead(zsRef.address()); + } + } + + /** + * Returns the total number of compressed bytes output so far. + * + *

Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now + * the preferred means of obtaining this information.

+ * + * @return the total number of compressed bytes output so far + */ + @Override + public int getTotalOut() { + return (int) getBytesWritten(); + } + + /** + * Returns the total number of compressed bytes output so far.

+ * + * @return the total (non-negative) number of compressed bytes output so far + * @since 1.5 + */ + @Override + public long getBytesWritten() { + synchronized (zsRef) { + ensureOpen(); + return getBytesWritten(zsRef.address()); + } + } + + /** + * Resets deflater so that a new set of input data can be processed. + * Keeps current compression level and strategy settings. + */ + @Override + public void reset() { + synchronized (zsRef) { + ensureOpen(); + reset(zsRef.address()); + finish = false; + finished = false; + off = len = 0; + } + } + + /** + * Closes the compressor and discards any unprocessed input. + * This method should be called when the compressor is no longer + * being used, but will also be called automatically by the + * finalize() method. Once this method is called, the behavior + * of the IntelDeflater object is undefined. + */ + @Override + public void end() { + synchronized (zsRef) { + long addr = zsRef.address(); + zsRef.clear(); + if (addr != 0) { + end(addr); + buf = null; + } + } + } + + /** + * Closes the compressor when garbage is collected. + */ + protected void finalize() { + end(); + } + + private void ensureOpen() { + assert Thread.holdsLock(zsRef); + if (zsRef.address() == 0) + throw new NullPointerException("IntelDeflater has been closed"); + } + + private static native void initIDs(); + private native static long init(int level, int strategy, boolean nowrap); + private native static void setDictionary(long addr, byte[] b, int off, int len); + private native int deflateBytes(long addr, byte[] b, int off, int len, + int flush); + private native static int getAdler(long addr); + private native static long getBytesRead(long addr); + private native static long getBytesWritten(long addr); + private native static void reset(long addr); + private native static void end(long addr); +} + diff --git a/src/java/net/sf/samtools/util/zip/ZStreamRef.java b/src/java/net/sf/samtools/util/zip/ZStreamRef.java new file mode 100644 index 0000000000..5a49d8a0a6 --- /dev/null +++ b/src/java/net/sf/samtools/util/zip/ZStreamRef.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package net.sf.samtools.util.zip; + +/** + * Copied from OpenJDK 7. Only change is package name, because IntelDeflater needs this and + * the original class is private to java.util.zip package. + * + * A reference to the native zlib's z_stream structure. + */ + +class ZStreamRef { + + private long address; + ZStreamRef (long address) { + this.address = address; + } + + long address() { + return address; + } + + void clear() { + address = 0; + } +} diff --git a/src/scripts/build_intel_deflater.sh b/src/scripts/build_intel_deflater.sh new file mode 100644 index 0000000000..49d018c30d --- /dev/null +++ b/src/scripts/build_intel_deflater.sh @@ -0,0 +1,64 @@ +#! /bin/bash +# +# The MIT License +# +# Copyright (c) 2013 The Broad Institute +# +# 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. +# + +# Build libIntelDeflater.so, the JNI library that wraps Intel IPP compression library. +# Note that this is not built as part of standard release process. Rather, it is built manually and then +# copied to Picard-public/lib/jni. + +# Assumes OpenJDK exists at $OPENJDK. I used openjdk-7-fcs-src-b147-27_jun_2011.zip +# Assumes that Picard-public java sources have been compiled +# Assumes IPP8_CODE_SAMPLES_DIR points to Intel IPP sample code built with -fPIC +set -e + +if [ "$OPENJDK" = "" ] +then echo "ERROR: OPENJDK environment variable not defined." >&2 + exit 1 +fi + +if [ "$IPP8_CODE_SAMPLES_DIR" = "" ] +then echo "ERROR: IPP8_CODE_SAMPLES_DIR environment variable not defined." >&2 + exit 1 +fi + +rootdir=$(dirname $(dirname $(dirname $0))) + + +builddir=$rootdir/lib_build +rm -rf $builddir +mkdir -p $builddir + +# Create JNI C header file +javah -jni -classpath $rootdir/classes -d $builddir net.sf.samtools.util.zip.IntelDeflater + +# Compile source and create library. +gcc -I$builddir -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ -I$OPENJDK/jdk/src/share/native/common/ \ +-I$OPENJDK/jdk/src/solaris/native/common/ -c -O3 -fPIC IntelDeflater.c +gcc -shared -o $builddir/libIntelDeflater.so IntelDeflater.o -L${IPP8_CODE_SAMPLES_DIR}/__cmake/data-compression.intel64.make.static.release/__lib/release \ +-lzlib -lstdc++ -Wl,-Bstatic -lbfp754 -ldecimal -liomp5 -liompstubs5 -lipgo -lippac -lippcc -lippch -lippcv \ +-lippdc -lippdi -lippgen -lippi -lippj -lippm -lippr -lippsc -lippvc -lippvm -lirng -lmatmul -lpdbx \ +-lpdbxinst -lsvml -lipps -limf -lirc -lirc_s -lippcore -Wl,-Bdynamic + + +