Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/com/bc/zarr/Compressor.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public abstract class Compressor {

public abstract void uncompress(InputStream is, OutputStream os) throws IOException;

void passThrough(InputStream is, OutputStream os) throws IOException {
protected void passThrough(InputStream is, OutputStream os) throws IOException {
final byte[] bytes = new byte[4096];
int read = is.read(bytes);
while (read >= 0) {
Expand Down
86 changes: 61 additions & 25 deletions src/main/java/com/bc/zarr/CompressorFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.Deflater;
Expand All @@ -52,6 +51,17 @@ public class CompressorFactory {

public final static Compressor nullCompressor = new NullCompressor();

private static final Map<String, Class<? extends Compressor>> registeredCompressors = defaultCompressors();

private static Map<String, Class<? extends Compressor>> defaultCompressors() {
Map<String, Class<? extends Compressor>> compressors = new HashMap<>();
compressors.put("null", NullCompressor.class);
compressors.put("zlib", ZlibCompressor.class);
compressors.put("blosc", BloscCompressor.class);

return compressors;
}

/**
* @return the properties of the default compressor as a key/value map.
*/
Expand All @@ -69,6 +79,26 @@ public static Map<String, Object> getDefaultCompressorProperties() {
return map;
}

/**
* Add or replace a Compressor class for a given id.
*
* @param id the type of the compression algorithm
* @param compressor the class to instantiate
*/
public static void registerCompressor(String id, Class<? extends Compressor> compressor) {
if (id != null) {
registeredCompressors.put(id, compressor);
}
}

public static String[] listRegisteredCompressorIds() {
return registeredCompressors.keySet().toArray(new String[0]);
}

public static Map<String, Class<? extends Compressor>> listRegisteredCompressors() {
return new HashMap<>(registeredCompressors);
}

/**
* @return a new Compressor instance using the method {@link #create(Map properties)} with {@link #getDefaultCompressorProperties()}.
*/
Expand All @@ -92,7 +122,7 @@ public static Compressor create(Map<String, Object> properties) {
* Creates a new {@link Compressor} instance according to the id and the given properties.
*
* @param id the type of the compression algorithm
* @param keyValuePair an even count of key value pairs defining the compressor specific properties
* @param keyValuePair an even count of key value pairs defining the compressor-specific properties
* @return a new Compressor instance according to the id and the properties
* @throws IllegalArgumentException If it is not able to create a Compressor.
*/
Expand All @@ -107,20 +137,27 @@ public static Compressor create(String id, Object... keyValuePair) {
* Creates a new {@link Compressor} instance according to the id and the given properties.
*
* @param id the type of the compression algorithm
* @param properties a Map containing the compressor specific properties
* @param properties a Map containing the compressor-specific properties
* @return a new Compressor instance according to the id and the properties
* @throws IllegalArgumentException If it is not able to create a Compressor.
*/
public static Compressor create(String id, Map<String, Object> properties) {
if ("null".equals(id)) {
return nullCompressor;
}
if ("zlib".equals(id)) {
return new ZlibCompressor(properties);
}
if ("blosc".equals(id)) {
return new BloscCompressor(properties);
try {
if (registeredCompressors.containsKey(id)) {
Class<? extends Compressor> c = registeredCompressors.get(id);
if (c.equals(NullCompressor.class)) {
return nullCompressor;
}
return c.getDeclaredConstructor(Map.class).newInstance(properties);
}
} catch (ReflectiveOperationException e) {
if (e.getCause() instanceof IllegalArgumentException) {
throw (IllegalArgumentException) e.getCause();
}

throw new IllegalArgumentException("Could not create compressor with id:'" + id + "'", e);
}

throw new IllegalArgumentException("Compressor id:'" + id + "' not supported.");
}

Expand Down Expand Up @@ -160,7 +197,7 @@ public void uncompress(InputStream is, OutputStream os) throws IOException {
private static class ZlibCompressor extends Compressor {
private final int level;

private ZlibCompressor(Map<String, Object> map) {
protected ZlibCompressor(Map<String, Object> map) {
final Object levelObj = map.get("level");
if (levelObj == null) {
this.level = 1; //default value
Expand Down Expand Up @@ -230,20 +267,20 @@ public static class BloscCompressor extends Compressor {
public final static String[] supportedCnames = new String[]{"zstd", "blosclz", defaultCname, "lz4hc", "zlib"/*, "snappy"*/};

public final static Map<String, Object> defaultProperties = new HashMap<String, Object>() {{
put(keyCname, defaultCname);
put(keyClevel, defaultCLevel);
put(keyShuffle, defaultShuffle);
put(keyBlocksize, defaultBlocksize);
put(keyNumThreads, defaultNumThreads);
}};
put(keyCname, defaultCname);
put(keyClevel, defaultCLevel);
put(keyShuffle, defaultShuffle);
put(keyBlocksize, defaultBlocksize);
put(keyNumThreads, defaultNumThreads);
}};

private final int clevel;
private final int blocksize;
private final int shuffle;
private final String cname;
private final int nthreads;

private BloscCompressor(Map<String, Object> map) {
protected BloscCompressor(Map<String, Object> map) {
final Object cnameObj = map.get(keyCname);
if (cnameObj == null) {
cname = defaultCname;
Expand Down Expand Up @@ -330,8 +367,8 @@ public int getNumThreads() {
@Override
public String toString() {
return "compressor=" + getId()
+ "/cname=" + cname + "/clevel=" + clevel
+ "/blocksize=" + blocksize + "/shuffle=" + shuffle;
+ "/cname=" + cname + "/clevel=" + clevel
+ "/blocksize=" + blocksize + "/shuffle=" + shuffle;
}

@Override
Expand Down Expand Up @@ -364,16 +401,15 @@ public void uncompress(InputStream is, OutputStream os) throws IOException {
os.write(outBuffer.array());
}

private BufferSizes cbufferSizes(ByteBuffer cbuffer) {
protected BufferSizes cbufferSizes(ByteBuffer cbuffer) {
NativeLongByReference nbytes = new NativeLongByReference();
NativeLongByReference cbytes = new NativeLongByReference();
NativeLongByReference blocksize = new NativeLongByReference();
IBloscDll.blosc_cbuffer_sizes(cbuffer, nbytes, cbytes, blocksize);
BufferSizes bs = new BufferSizes(nbytes.getValue().longValue(),
cbytes.getValue().longValue(),
blocksize.getValue().longValue());
cbytes.getValue().longValue(),
blocksize.getValue().longValue());
return bs;
}
}
}

2 changes: 1 addition & 1 deletion src/test/java/com/bc/zarr/BloscCompressorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ public class BloscCompressorTest {
public static void beforeClass() {

final String bloscJnaLibraryPath = System.getProperty("bloscJnaLibraryPath");
System.err.println(bloscJnaLibraryPath);
final boolean bloscPathDefined = bloscJnaLibraryPath != null;
final boolean bloscAvailable = isBloscAvailable();
if (bloscPathDefined && !bloscAvailable) {
System.err.println(bloscJnaLibraryPath);
Assert.fail("Property for blosc has been configured, but blosc is not available.");
} else {
Assume.assumeTrue("Blosc library path is not configured. Blosc specifc tests are not executed.", bloscAvailable); // test is skipped if blosc is not available
Expand Down
165 changes: 92 additions & 73 deletions src/test/java/com/bc/zarr/CompressorFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,14 @@ public void create_compressor_not_supported() {

@Test
public void createBloscValidCnames() {
String[] cnames = { "zstd", "blosclz", "lz4", "lz4hc", "zlib" };
String[] cnames = {"zstd", "blosclz", "lz4", "lz4hc", "zlib"};
for (int i = 0; i < cnames.length; i += 1) {
final Compressor compressor = CompressorFactory.create("blosc", "cname", cnames[i]);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=" + cnames[i] +
"/clevel=5/blocksize=0/shuffle=1", compressor.toString());
"compressor=blosc/cname=" + cnames[i] +
"/clevel=5/blocksize=0/shuffle=1", compressor.toString());
}
}

Expand All @@ -141,74 +141,93 @@ public void createBloscInvalidCname() {
} catch (IllegalArgumentException expected) {
assertEquals("blosc: compressor not supported: 'unsupported'; expected one of [zstd, blosclz, lz4, lz4hc, zlib]", expected.getMessage());
}
}

@Test
public void createBloscValidClevel() {
final Compressor compressor = CompressorFactory.create("blosc", "clevel", 1);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=1/blocksize=0/shuffle=1", compressor.toString());
}

@Test
public void createBloscInvalidClevel() {
try {
CompressorFactory.create("blosc", "clevel", -1);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals("blosc: clevel parameter must be between 0 and 9 but was: -1", expected.getMessage());
}

try {
CompressorFactory.create("blosc", "clevel", 10);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals(
"blosc: clevel parameter must be between 0 and 9 but was: 10",
expected.getMessage());
}
}

@Test
public void createBloscValidShuffles() {
int[] shuffles = { 0, 1, 2 };
for (int i = 0; i < shuffles.length; i += 1) {
final Compressor compressor = CompressorFactory.create("blosc", "shuffle", shuffles[i]);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=5/blocksize=0/shuffle=" +
shuffles[i], compressor.toString());
}
}

@Test
public void createBloscInvalidShuffle() {
try {
CompressorFactory.create("blosc", "shuffle", -1);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals(
"blosc: shuffle type not supported: '-1'; expected one of [0 (NOSHUFFLE), 1 (BYTESHUFFLE), 2 (BITSHUFFLE)]",
expected.getMessage());
}
}

@Test
public void createBloscValidBlockSizes() {
int[] blockSizes = { 0, 1, 20 };
for (int i = 0; i < blockSizes.length; i += 1) {
final Compressor compressor = CompressorFactory.create("blosc", "blocksize", blockSizes[i]);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=5/blocksize=" + blockSizes[i] +
"/shuffle=1", compressor.toString());
}
}
}

@Test
public void createBloscValidClevel() {
final Compressor compressor = CompressorFactory.create("blosc", "clevel", 1);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=1/blocksize=0/shuffle=1", compressor.toString());
}

@Test
public void createBloscInvalidClevel() {
try {
CompressorFactory.create("blosc", "clevel", -1);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals("blosc: clevel parameter must be between 0 and 9 but was: -1", expected.getMessage());
}

try {
CompressorFactory.create("blosc", "clevel", 10);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals(
"blosc: clevel parameter must be between 0 and 9 but was: 10",
expected.getMessage());
}
}

@Test
public void createBloscValidShuffles() {
int[] shuffles = {0, 1, 2};
for (int i = 0; i < shuffles.length; i += 1) {
final Compressor compressor = CompressorFactory.create("blosc", "shuffle", shuffles[i]);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=5/blocksize=0/shuffle=" +
shuffles[i], compressor.toString());
}
}

@Test
public void createBloscInvalidShuffle() {
try {
CompressorFactory.create("blosc", "shuffle", -1);
fail("IllegalArgumentException expected");
} catch (IllegalArgumentException expected) {
assertEquals(
"blosc: shuffle type not supported: '-1'; expected one of [0 (NOSHUFFLE), 1 (BYTESHUFFLE), 2 (BITSHUFFLE)]",
expected.getMessage());
}
}

@Test
public void createBloscValidBlockSizes() {
int[] blockSizes = {0, 1, 20};
for (int i = 0; i < blockSizes.length; i += 1) {
final Compressor compressor = CompressorFactory.create("blosc", "blocksize", blockSizes[i]);
assertNotNull(compressor);
assertEquals("blosc", compressor.getId());
assertEquals(
"compressor=blosc/cname=lz4" +
"/clevel=5/blocksize=" + blockSizes[i] +
"/shuffle=1", compressor.toString());
}
}

@Test
public void registerNewCompressor() {
final String id = "test";
CompressorFactory.registerCompressor(id, TestUtils.TestCompressor.class);
Compressor compressor = CompressorFactory.create(id, TestUtils.createMap("level", 1));
assertNotNull(compressor);
assertEquals(id, compressor.getId());
assertEquals(TestUtils.TestCompressor.class, compressor.getClass());CompressorFactory.registerCompressor(id, TestUtils.TestCompressor.class);

// Replacement
CompressorFactory.registerCompressor(id, CompressorFactory.BloscCompressor.class);
compressor = CompressorFactory.create(id);
assertNotNull(compressor);
// Expected not to match.
assertEquals("blosc", compressor.getId());
assertEquals(CompressorFactory.BloscCompressor.class, compressor.getClass());

}
}
19 changes: 19 additions & 0 deletions src/test/java/com/bc/zarr/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,23 @@ public static boolean deleteLineContaining(String str, InputStream is, OutputStr
}
return deleted;
}

public static class TestCompressor extends Compressor {
public TestCompressor(Map<String, Object> properties) {
}

public String getId() {
return "test";
}

public String toString() {
return getId();
}

public void compress(InputStream is, OutputStream os) throws IOException {
}

public void uncompress(InputStream is, OutputStream os) throws IOException {
}
}
}