Skip to content
Commits on Source (5)
Since version 1.1.0.x, Java 6 (1.6) or higher is required.
## snappy-java-1.1.7.2 (2018-05-21)
* Fix for aarch64 endian issue
## snappy-java-1.1.7.1 (2017-12-07)
* Fix for Android. No need to upgrade to this version if you are not using Android
......
snappy-java [![Build Status](https://travis-ci.org/xerial/snappy-java.svg?branch=master)](https://travis-ci.org/xerial/snappy-java) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.xerial.snappy/snappy-java/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.xerial.snappy/snappy-java/) [![Javadoc](http://javadoc-badge.appspot.com/org.xerial.snappy/snappy-java.svg?label=javadoc)](http://javadoc-badge.appspot.com/org.xerial.snappy/snappy-java) [![Reference Status](https://www.versioneye.com/java/org.xerial.snappy:snappy-java/reference_badge.svg?style=flat-square)](https://www.versioneye.com/java/org.xerial.snappy:snappy-java/references)
snappy-java [![Build Status](https://travis-ci.org/xerial/snappy-java.svg?branch=master)](https://travis-ci.org/xerial/snappy-java) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.xerial.snappy/snappy-java/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.xerial.snappy/snappy-java/) [![Javadoc](http://javadoc-badge.appspot.com/org.xerial.snappy/snappy-java.svg?label=javadoc)](http://javadoc-badge.appspot.com/org.xerial.snappy/snappy-java)
===
snappy-java is a Java port of the snappy
<http://code.google.com/p/snappy/>, a fast C++ compresser/decompresser developed by Google.
......@@ -83,9 +83,20 @@ Stream-based compressor/decompressor `SnappyOutputStream`/`SnappyInputStream` ar
* See also [Javadoc API](https://oss.sonatype.org/service/local/repositories/releases/archive/org/xerial/snappy/snappy-java/1.1.3-M1/snappy-java-1.1.3-M1-javadoc.jar/!/index.html)
#### Compatibility Notes
* `SnappyOutputStream` and `SnappyInputStream` use `[magic header:16 bytes]([block size:int32][compressed data:byte array])*` format. You can read the result of `Snappy.compress` with `SnappyInputStream`, but you cannot read the compressed data generated by `SnappyOutputStream` with `Snappy.uncompress`. Here is the data format compatibility matrix:
The original Snappy format definition did not define a file format. It later added
a "framing" format to define a file format, but by this point major software was
already using an industry standard instead -- represented in this library by the
`SnappyOutputStream` and `SnappyInputStream` methods.
For interoperability with other libraries, check that compatible formats are used.
Note that not all libraries support all variants.
* `SnappyOutputStream` and `SnappyInputStream` use `[magic header:16 bytes]([block size:int32][compressed data:byte array])*` format. You can read the result of `Snappy.compress` with `SnappyInputStream`, but you cannot read the compressed data generated by `SnappyOutputStream` with `Snappy.uncompress`.
* `SnappyHadoopCompatibleOutputStream` does not emit a file header but write out the current block size as a preemble to each block
#### Data format compatibility matrix:
| Write\Read | `Snappy.uncompress` | `SnappyInputStream` | `SnappyFramedInputStream` | `org.apache.hadoop.io.compress.SnappyCodec` |
| --------------- |:-------------------:|:------------------:|:-----------------------:|:-------------------------------------------:|
| `Snappy.compress` | ok | ok | x | x |
......
snappy-java (1.1.7.3-1) unstable; urgency=medium
* Team upload.
* New upstream release
* Standards-Version updated to 4.5.0
-- Emmanuel Bourg <ebourg@apache.org> Sun, 26 Jan 2020 19:47:06 +0100
snappy-java (1.1.7.2-1) unstable; urgency=medium
* Team upload.
......
......@@ -10,7 +10,7 @@ Build-Depends:
libmaven-bundle-plugin-java,
libsnappy-dev,
maven-debian-helper
Standards-Version: 4.2.1
Standards-Version: 4.5.0
Vcs-Browser: https://salsa.debian.org/java-team/snappy-java
Vcs-Git: https://salsa.debian.org/java-team/snappy-java.git
Homepage: https://github.com/xerial/snappy-java
......
......@@ -3,10 +3,22 @@
*/
package org.xerial.snappy;
import static java.lang.invoke.MethodHandles.constant;
import static java.lang.invoke.MethodHandles.dropArguments;
import static java.lang.invoke.MethodHandles.filterReturnValue;
import static java.lang.invoke.MethodHandles.guardWithTest;
import static java.lang.invoke.MethodHandles.lookup;
import static java.lang.invoke.MethodType.methodType;
import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -30,27 +42,76 @@ final class SnappyFramed
* Sun specific mechanisms to clean up resources associated with direct byte buffers.
*/
@SuppressWarnings("unchecked")
private static final Class<? extends ByteBuffer> SUN_DIRECT_BUFFER = (Class<? extends ByteBuffer>) lookupClassQuietly("sun.nio.ch.DirectBuffer");
private static final Method SUN_BUFFER_CLEANER;
private static final Method SUN_CLEANER_CLEAN;
static final Class<? extends ByteBuffer> DIRECT_BUFFER_CLAZZ = (Class<? extends ByteBuffer>) lookupClassQuietly("java.nio.DirectByteBuffer");
static final MethodHandle CLEAN_HANDLE;
static {
Method bufferCleaner = null;
Method cleanerClean = null;
// this approach is based off that used by apache lucene and documented here: https://issues.apache.org/jira/browse/LUCENE-6989
// and https://github.com/apache/lucene-solr/blob/7e03427fa14a024ce257babcb8362d2451941e21/lucene/core/src/java/org/apache/lucene/store/MMapDirectory.java
MethodHandle cleanHandle = null;
try {
final PrivilegedExceptionAction<MethodHandle> action = new PrivilegedExceptionAction<MethodHandle>() {
@Override
public MethodHandle run() throws Exception {
MethodHandle handle = null;
if (DIRECT_BUFFER_CLAZZ != null) {
final Lookup lookup = lookup();
try {
//operate under the assumption that if the sun direct buffer class exists,
//all of the sun classes exist
if (SUN_DIRECT_BUFFER != null) {
bufferCleaner = SUN_DIRECT_BUFFER.getMethod("cleaner", (Class[]) null);
Class<?> cleanClazz = lookupClassQuietly("sun.misc.Cleaner");
cleanerClean = cleanClazz.getMethod("clean", (Class[]) null);
// sun.misc.Unsafe unmapping (Java 9+)
final Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
// first check if Unsafe has the right method, otherwise we can give up
// without doing any security critical stuff:
final MethodHandle unmapper = lookup.findVirtual(unsafeClass, "invokeCleaner", methodType(void.class, ByteBuffer.class));
// fetch the unsafe instance and bind it to the virtual MH:
final Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
final Object theUnsafe = f.get(null);
handle = unmapper.bindTo(theUnsafe);
} catch (Exception e) {
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "unable to use java 9 Unsafe.invokeCleaner", e);
// sun.misc.Cleaner unmapping (Java 8 and older)
final Method m = DIRECT_BUFFER_CLAZZ.getMethod("cleaner");
m.setAccessible(true);
final MethodHandle directBufferCleanerMethod = lookup.unreflect(m);
final Class<?> cleanerClass = directBufferCleanerMethod.type().returnType();
/*
* "Compile" a MethodHandle that basically is equivalent to the following code:
* void unmapper(ByteBuffer byteBuffer)
* {
* sun.misc.Cleaner cleaner = ((java.nio.DirectByteBuffer) byteBuffer).cleaner();
* if (nonNull(cleaner))
* {
* cleaner.clean();
* }
* else
* {
* // the noop is needed because MethodHandles#guardWithTest always needs ELSE
* noop(cleaner);
* }
* }
*/
final MethodHandle cleanMethod = lookup.findVirtual(cleanerClass, "clean", methodType(void.class));
final MethodHandle nonNullTest = lookup.findStatic(SnappyFramed.class, "nonNull", methodType(boolean.class, Object.class)).asType(methodType(boolean.class, cleanerClass));
final MethodHandle noop = dropArguments(constant(Void.class, null).asType(methodType(void.class)), 0, cleanerClass);
handle = filterReturnValue(directBufferCleanerMethod, guardWithTest(nonNullTest, cleanMethod, noop)).asType(methodType(void.class, ByteBuffer.class));
}
}
catch (Throwable t) {
return handle;
}
};
cleanHandle = AccessController.doPrivileged(action);
} catch (Throwable t) {
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to lookup Sun specific DirectByteBuffer cleaner classes.", t);
}
SUN_BUFFER_CLEANER = bufferCleaner;
SUN_CLEANER_CLEAN = cleanerClean;
CLEAN_HANDLE = cleanHandle;
}
/**
......@@ -170,18 +231,34 @@ final class SnappyFramed
*
* @param buffer The {@code ByteBuffer} to release. Must not be {@code null}. Must be {@link ByteBuffer#isDirect() direct}.
*/
static void releaseDirectByteBuffer(ByteBuffer buffer)
static void releaseDirectByteBuffer(final ByteBuffer buffer)
{
assert buffer != null && buffer.isDirect();
if (SUN_DIRECT_BUFFER != null && SUN_DIRECT_BUFFER.isAssignableFrom(buffer.getClass())) {
if (CLEAN_HANDLE != null && DIRECT_BUFFER_CLAZZ.isInstance(buffer)) {
try {
final PrivilegedExceptionAction<Void> pea = new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws Exception {
try {
Object cleaner = SUN_BUFFER_CLEANER.invoke(buffer, (Object[]) null);
SUN_CLEANER_CLEAN.invoke(cleaner, (Object[]) null);
CLEAN_HANDLE.invokeExact(buffer);
} catch (Exception e) {
throw e;
} catch (Throwable t) {
//this will be an error
throw new RuntimeException(t);
}
catch (Throwable t) {
return null;
}
};
AccessController.doPrivileged(pea);
} catch (Throwable t) {
Logger.getLogger(SnappyFramed.class.getName()).log(Level.FINE, "Exception occurred attempting to clean up Sun specific DirectByteBuffer.", t);
}
}
}
static boolean nonNull(Object o) {
return o != null;
}
}
......@@ -365,22 +365,18 @@ public class SnappyOutputStream
protected void compressInput()
throws IOException
{
if (inputCursor <= 0) {
return; // no need to dump
}
if (!headerWritten) {
outputCursor = writeHeader();
headerWritten = true;
}
if (inputCursor <= 0) {
return; // no need to dump
}
// Compress and dump the buffer content
if (!hasSufficientOutputBufferFor(inputCursor)) {
dumpOutput();
}
writeBlockPreemble();
int compressedSize = Snappy.compress(inputBuffer, 0, inputCursor, outputBuffer, outputCursor + 4);
// Write compressed data size
writeInt(outputBuffer, outputCursor, compressedSize);
......
version in ThisBuild := "1.1.7.2"
version in ThisBuild := "1.1.7.3"