commit
50930c8c3b
|
@ -165,6 +165,14 @@ snappy-java uses sbt (simple build tool for Scala) as a build tool. Here is a si
|
||||||
> ~test-only * # run tests that matches a given name pattern
|
> ~test-only * # run tests that matches a given name pattern
|
||||||
> publishM2 # publish jar to $HOME/.m2/repository
|
> publishM2 # publish jar to $HOME/.m2/repository
|
||||||
> package # create jar file
|
> package # create jar file
|
||||||
|
> findbugs # Produce findbugs report in target/findbugs
|
||||||
|
> jacoco:cover # Report the code coverage of tests to target/jacoco folder
|
||||||
|
|
||||||
|
If you need to see detailed debug messages, launch sbt with `-Dloglevel=debug` option:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./sbt -Dloglevel=debug
|
||||||
|
```
|
||||||
|
|
||||||
For the details of sbt usage, see my blog post: [Building Java Projects with sbt](http://xerial.org/blog/2014/03/24/sbt/)
|
For the details of sbt usage, see my blog post: [Building Java Projects with sbt](http://xerial.org/blog/2014/03/24/sbt/)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import de.johoop.findbugs4sbt.ReportType
|
||||||
|
|
||||||
name := "snappy-java"
|
name := "snappy-java"
|
||||||
|
|
||||||
|
@ -68,6 +69,14 @@ logBuffered in Test := false
|
||||||
|
|
||||||
incOptions := incOptions.value.withNameHashing(true)
|
incOptions := incOptions.value.withNameHashing(true)
|
||||||
|
|
||||||
|
findbugsSettings
|
||||||
|
|
||||||
|
findbugsReportType := Some(ReportType.FancyHtml)
|
||||||
|
|
||||||
|
findbugsReportPath := Some(crossTarget.value / "findbugs" / "report.html")
|
||||||
|
|
||||||
|
jacoco.settings
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"junit" % "junit" % "4.8.2" % "test",
|
"junit" % "junit" % "4.8.2" % "test",
|
||||||
"org.codehaus.plexus" % "plexus-classworlds" % "2.4" % "test",
|
"org.codehaus.plexus" % "plexus-classworlds" % "2.4" % "test",
|
||||||
|
|
|
@ -5,7 +5,7 @@ addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "0.5.0")
|
||||||
|
|
||||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
|
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
|
||||||
|
|
||||||
addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.3.0")
|
addSbtPlugin("de.johoop" % "findbugs4sbt" % "1.4.0")
|
||||||
|
|
||||||
addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.5")
|
addSbtPlugin("de.johoop" % "jacoco4sbt" % "2.1.5")
|
||||||
|
|
||||||
|
|
|
@ -476,7 +476,7 @@ public class Snappy
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
byte[] result = new byte[Snappy.uncompressedLength(input)];
|
byte[] result = new byte[Snappy.uncompressedLength(input)];
|
||||||
int byteSize = Snappy.uncompress(input, 0, input.length, result, 0);
|
Snappy.uncompress(input, 0, input.length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
||||||
char[] result = new char[uncompressedLength / 2];
|
char[] result = new char[uncompressedLength / 2];
|
||||||
int byteSize = impl.rawUncompress(input, offset, length, result, 0);
|
impl.rawUncompress(input, offset, length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +585,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, 0, input.length);
|
int uncompressedLength = Snappy.uncompressedLength(input, 0, input.length);
|
||||||
double[] result = new double[uncompressedLength / 8];
|
double[] result = new double[uncompressedLength / 8];
|
||||||
int byteSize = impl.rawUncompress(input, 0, input.length, result, 0);
|
impl.rawUncompress(input, 0, input.length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +687,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
||||||
float[] result = new float[uncompressedLength / 4];
|
float[] result = new float[uncompressedLength / 4];
|
||||||
int byteSize = impl.rawUncompress(input, offset, length, result, 0);
|
impl.rawUncompress(input, offset, length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +718,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
||||||
int[] result = new int[uncompressedLength / 4];
|
int[] result = new int[uncompressedLength / 4];
|
||||||
int byteSize = impl.rawUncompress(input, offset, length, result, 0);
|
impl.rawUncompress(input, offset, length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,7 +749,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
||||||
long[] result = new long[uncompressedLength / 8];
|
long[] result = new long[uncompressedLength / 8];
|
||||||
int byteSize = impl.rawUncompress(input, offset, length, result, 0);
|
impl.rawUncompress(input, offset, length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,7 +780,7 @@ public class Snappy
|
||||||
{
|
{
|
||||||
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
int uncompressedLength = Snappy.uncompressedLength(input, offset, length);
|
||||||
short[] result = new short[uncompressedLength / 2];
|
short[] result = new short[uncompressedLength / 2];
|
||||||
int byteSize = impl.rawUncompress(input, offset, length, result, 0);
|
impl.rawUncompress(input, offset, length, result, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,7 +838,7 @@ public class Snappy
|
||||||
UnsupportedEncodingException
|
UnsupportedEncodingException
|
||||||
{
|
{
|
||||||
byte[] uncompressed = new byte[uncompressedLength(input, offset, length)];
|
byte[] uncompressed = new byte[uncompressedLength(input, offset, length)];
|
||||||
int compressedSize = uncompress(input, offset, length, uncompressed, 0);
|
uncompress(input, offset, length, uncompressed, 0);
|
||||||
return new String(uncompressed, encoding);
|
return new String(uncompressed, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,7 +858,7 @@ public class Snappy
|
||||||
UnsupportedEncodingException
|
UnsupportedEncodingException
|
||||||
{
|
{
|
||||||
byte[] uncompressed = new byte[uncompressedLength(input, offset, length)];
|
byte[] uncompressed = new byte[uncompressedLength(input, offset, length)];
|
||||||
int compressedSize = uncompress(input, offset, length, uncompressed, 0);
|
uncompress(input, offset, length, uncompressed, 0);
|
||||||
return new String(uncompressed, encoding);
|
return new String(uncompressed, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,10 @@ import java.util.Arrays;
|
||||||
*/
|
*/
|
||||||
public class SnappyCodec
|
public class SnappyCodec
|
||||||
{
|
{
|
||||||
public static final byte[] MAGIC_HEADER = new byte[] {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0};
|
static final byte[] MAGIC_HEADER = new byte[] {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0};
|
||||||
public static final int MAGIC_LEN = MAGIC_HEADER.length;
|
public static final int MAGIC_LEN = MAGIC_HEADER.length;
|
||||||
public static final int HEADER_SIZE = MAGIC_LEN + 8;
|
public static final int HEADER_SIZE = MAGIC_LEN + 8;
|
||||||
public static final int MAGIC_HEADER_HEAD = SnappyOutputStream.readInt(MAGIC_HEADER, 0);
|
public static final int MAGIC_HEADER_HEAD = SnappyOutputStream.readInt(MAGIC_HEADER, 0);
|
||||||
public static final int MAGIC_HEADER_TAIL = SnappyOutputStream.readInt(MAGIC_HEADER, 4);
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
assert (MAGIC_HEADER_HEAD < 0);
|
assert (MAGIC_HEADER_HEAD < 0);
|
||||||
|
@ -60,6 +59,7 @@ public class SnappyCodec
|
||||||
|
|
||||||
public static final int DEFAULT_VERSION = 1;
|
public static final int DEFAULT_VERSION = 1;
|
||||||
public static final int MINIMUM_COMPATIBLE_VERSION = 1;
|
public static final int MINIMUM_COMPATIBLE_VERSION = 1;
|
||||||
|
public static final SnappyCodec currentHeader = new SnappyCodec(MAGIC_HEADER, DEFAULT_VERSION, MINIMUM_COMPATIBLE_VERSION);
|
||||||
|
|
||||||
public final byte[] magic;
|
public final byte[] magic;
|
||||||
public final int version;
|
public final int version;
|
||||||
|
@ -86,6 +86,11 @@ public class SnappyCodec
|
||||||
headerArray = header.toByteArray();
|
headerArray = header.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] getMagicHeader()
|
||||||
|
{
|
||||||
|
return MAGIC_HEADER.clone();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
@ -125,6 +130,5 @@ public class SnappyCodec
|
||||||
int compatibleVersion = d.readInt();
|
int compatibleVersion = d.readInt();
|
||||||
return new SnappyCodec(magic, version, compatibleVersion);
|
return new SnappyCodec(magic, version, compatibleVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SnappyCodec currentHeader = new SnappyCodec(MAGIC_HEADER, DEFAULT_VERSION, MINIMUM_COMPATIBLE_VERSION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,11 +95,6 @@ public class SnappyInputStream
|
||||||
}
|
}
|
||||||
if (readBytes < header.length || header[0] != SnappyCodec.MAGIC_HEADER[0]) {
|
if (readBytes < header.length || header[0] != SnappyCodec.MAGIC_HEADER[0]) {
|
||||||
// do the default uncompression
|
// do the default uncompression
|
||||||
readFully(header, readBytes);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidHeader(header)) {
|
|
||||||
// (probably) compressed by Snappy.compress(byte[])
|
// (probably) compressed by Snappy.compress(byte[])
|
||||||
readFully(header, readBytes);
|
readFully(header, readBytes);
|
||||||
return;
|
return;
|
||||||
|
@ -393,8 +388,8 @@ public class SnappyInputStream
|
||||||
// Concatenated data
|
// Concatenated data
|
||||||
int remainingHeaderSize = SnappyCodec.headerSize() - 4;
|
int remainingHeaderSize = SnappyCodec.headerSize() - 4;
|
||||||
readBytes = readNext(header, 4, remainingHeaderSize);
|
readBytes = readNext(header, 4, remainingHeaderSize);
|
||||||
if (readBytes < remainingHeaderSize) {
|
if(readBytes < remainingHeaderSize) {
|
||||||
return false;
|
throw new SnappyIOException(SnappyErrorCode.FAILED_TO_UNCOMPRESS, String.format("Insufficient header size in a concatenated block"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isValidHeader(header)) {
|
if (isValidHeader(header)) {
|
||||||
|
|
|
@ -87,7 +87,10 @@ public class SnappyLoader
|
||||||
static void cleanUpExtractedNativeLib()
|
static void cleanUpExtractedNativeLib()
|
||||||
{
|
{
|
||||||
if (nativeLibFile != null && nativeLibFile.exists()) {
|
if (nativeLibFile != null && nativeLibFile.exists()) {
|
||||||
nativeLibFile.delete();
|
boolean deleted = nativeLibFile.delete();
|
||||||
|
if (!deleted) {
|
||||||
|
// Deleting native lib has failed, but it's not serious so simply ignore it here
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,37 +220,50 @@ public class SnappyLoader
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract a native library file into the target directory
|
// Extract a native library file into the target directory
|
||||||
InputStream reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
|
InputStream reader = null;
|
||||||
FileOutputStream writer = new FileOutputStream(extractedLibFile);
|
FileOutputStream writer = null;
|
||||||
try {
|
try {
|
||||||
byte[] buffer = new byte[8192];
|
reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
|
||||||
int bytesRead = 0;
|
try {
|
||||||
while ((bytesRead = reader.read(buffer)) != -1) {
|
writer = new FileOutputStream(extractedLibFile);
|
||||||
writer.write(buffer, 0, bytesRead);
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead = 0;
|
||||||
|
while ((bytesRead = reader.read(buffer)) != -1) {
|
||||||
|
writer.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (writer != null) {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// Delete the extracted lib file on JVM exit.
|
|
||||||
extractedLibFile.deleteOnExit();
|
|
||||||
|
|
||||||
if (writer != null) {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
if (reader != null) {
|
if (reader != null) {
|
||||||
reader.close();
|
reader.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete the extracted lib file on JVM exit.
|
||||||
|
extractedLibFile.deleteOnExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set executable (x) flag to enable Java to load the native library
|
// Set executable (x) flag to enable Java to load the native library
|
||||||
extractedLibFile.setReadable(true);
|
boolean success = extractedLibFile.setReadable(true) &&
|
||||||
extractedLibFile.setWritable(true, true);
|
extractedLibFile.setWritable(true, true) &&
|
||||||
extractedLibFile.setExecutable(true);
|
extractedLibFile.setExecutable(true);
|
||||||
|
if (!success) {
|
||||||
|
// Setting file flag may fail, but in this case another error will be thrown in later phase
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether the contents are properly copied from the resource folder
|
// Check whether the contents are properly copied from the resource folder
|
||||||
{
|
{
|
||||||
InputStream nativeIn = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
|
InputStream nativeIn = null;
|
||||||
InputStream extractedLibIn = new FileInputStream(extractedLibFile);
|
InputStream extractedLibIn = null;
|
||||||
try {
|
try {
|
||||||
|
nativeIn = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
|
||||||
|
extractedLibIn = new FileInputStream(extractedLibFile);
|
||||||
|
|
||||||
if (!contentsEquals(nativeIn, extractedLibIn)) {
|
if (!contentsEquals(nativeIn, extractedLibIn)) {
|
||||||
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, String.format("Failed to write a native library file at %s", extractedLibFile));
|
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, String.format("Failed to write a native library file at %s", extractedLibFile));
|
||||||
}
|
}
|
||||||
|
@ -318,7 +334,10 @@ public class SnappyLoader
|
||||||
// Temporary folder for the native lib. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
|
// Temporary folder for the native lib. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
|
||||||
File tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR, System.getProperty("java.io.tmpdir")));
|
File tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR, System.getProperty("java.io.tmpdir")));
|
||||||
if (!tempFolder.exists()) {
|
if (!tempFolder.exists()) {
|
||||||
tempFolder.mkdir();
|
boolean created = tempFolder.mkdirs();
|
||||||
|
if (!created) {
|
||||||
|
// if created == false, it will fail eventually in the later part
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract and load a native library inside the jar file
|
// Extract and load a native library inside the jar file
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class SnappyOutputStream
|
||||||
*/
|
*/
|
||||||
public SnappyOutputStream(OutputStream out, int blockSize)
|
public SnappyOutputStream(OutputStream out, int blockSize)
|
||||||
{
|
{
|
||||||
this(out, blockSize, CachedBufferAllocator.factory);
|
this(out, blockSize, CachedBufferAllocator.getBufferAllocatorFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SnappyOutputStream(OutputStream out, int blockSize, BufferAllocatorFactory bufferAllocatorFactory)
|
public SnappyOutputStream(OutputStream out, int blockSize, BufferAllocatorFactory bufferAllocatorFactory)
|
||||||
|
|
|
@ -9,8 +9,7 @@ import java.util.*;
|
||||||
public class CachedBufferAllocator
|
public class CachedBufferAllocator
|
||||||
implements BufferAllocator
|
implements BufferAllocator
|
||||||
{
|
{
|
||||||
|
private static BufferAllocatorFactory factory = new BufferAllocatorFactory()
|
||||||
public static BufferAllocatorFactory factory = new BufferAllocatorFactory()
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public BufferAllocator getBufferAllocator(int bufferSize)
|
public BufferAllocator getBufferAllocator(int bufferSize)
|
||||||
|
@ -19,10 +18,21 @@ public class CachedBufferAllocator
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static void setBufferAllocatorFactory(BufferAllocatorFactory factory)
|
||||||
|
{
|
||||||
|
assert (factory != null);
|
||||||
|
CachedBufferAllocator.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferAllocatorFactory getBufferAllocatorFactory()
|
||||||
|
{
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use SoftReference so that having this queueTable does not prevent the GC of CachedBufferAllocator instances
|
* Use SoftReference so that having this queueTable does not prevent the GC of CachedBufferAllocator instances
|
||||||
*/
|
*/
|
||||||
public static Map<Integer, SoftReference<CachedBufferAllocator>> queueTable = new HashMap<Integer, SoftReference<CachedBufferAllocator>>();
|
private static final Map<Integer, SoftReference<CachedBufferAllocator>> queueTable = new HashMap<Integer, SoftReference<CachedBufferAllocator>>();
|
||||||
|
|
||||||
private final int bufferSize;
|
private final int bufferSize;
|
||||||
private final Deque<byte[]> bufferQueue;
|
private final Deque<byte[]> bufferQueue;
|
||||||
|
|
|
@ -35,7 +35,6 @@ import java.io.InputStream;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.xerial.util.FileResource;
|
import org.xerial.util.FileResource;
|
||||||
import org.xerial.util.log.Logger;
|
import org.xerial.util.log.Logger;
|
||||||
import scala.Array;
|
|
||||||
|
|
||||||
public class SnappyInputStreamTest
|
public class SnappyInputStreamTest
|
||||||
{
|
{
|
||||||
|
@ -193,4 +192,16 @@ public class SnappyInputStreamTest
|
||||||
assertArrayEquals(orig1, uncompressed1);
|
assertArrayEquals(orig1, uncompressed1);
|
||||||
assertArrayEquals(orig2, uncompressed2);
|
assertArrayEquals(orig2, uncompressed2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readSnappyCompressResult()
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
byte[] orig = readResourceFile("alice29.txt");
|
||||||
|
byte[] compressed = Snappy.compress(orig);
|
||||||
|
SnappyInputStream in = new SnappyInputStream(new ByteArrayInputStream(compressed));
|
||||||
|
byte[] uncompressed = readFully(in);
|
||||||
|
|
||||||
|
assertArrayEquals(orig, uncompressed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ public class SnappyOutputStreamTest
|
||||||
// Regression test for issue #107, a bug where close() was non-idempotent and would release
|
// Regression test for issue #107, a bug where close() was non-idempotent and would release
|
||||||
// its buffers to the allocator multiple times, which could cause scenarios where two open
|
// its buffers to the allocator multiple times, which could cause scenarios where two open
|
||||||
// SnappyOutputStreams could share the same buffers, leading to stream corruption issues.
|
// SnappyOutputStreams could share the same buffers, leading to stream corruption issues.
|
||||||
final BufferAllocatorFactory bufferAllocatorFactory = CachedBufferAllocator.factory;
|
final BufferAllocatorFactory bufferAllocatorFactory = CachedBufferAllocator.getBufferAllocatorFactory();
|
||||||
final int BLOCK_SIZE = 4096;
|
final int BLOCK_SIZE = 4096;
|
||||||
// Create a stream, use it, then close it once:
|
// Create a stream, use it, then close it once:
|
||||||
ByteArrayOutputStream ba1 = new ByteArrayOutputStream();
|
ByteArrayOutputStream ba1 = new ByteArrayOutputStream();
|
||||||
|
|
Loading…
Reference in New Issue