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
+
+
+