de.jarnbjo.jsnappy.SnzMTInputStream Maven / Gradle / Ivy
Show all versions of bytecompressor-jsnappy_2.9 Show documentation
package de.jarnbjo.jsnappy;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
*
* Alternative to SnzInputStream
with support for multi-threaded decomression.
*
*
*
* By default, SnzMTInputStream will use a default ThreadPoolExecutor with a core size
* set to the number of available CPUs and a maximum size of twice the number of available
* CPUs. The threads used yby the default executor will be started as daemon threads.
* The default executor is not created until actually requried and can be queried and
* replaced with the static getDefaultExecutor
and setDefaultExecutor
* methods.
*
*
* @author Tor-Einar Jarnbjo
* @since 1.0
*/
public class SnzMTInputStream extends SnzInputStream {
private static final int AVAILABLE_CPUS = Runtime.getRuntime().availableProcessors();
private static Executor defaultExecutor = null;
private boolean meEof = false, delegateEof = false;
private LinkedList tasks = new LinkedList();
private Buffer dbuffer;
private int dbufferIndex = 0;
private Executor executor;
/**
* Creates an input stream, which reads from the specified input
* and uses the default executor.
* @param in
*/
public SnzMTInputStream(InputStream in) {
this(in, initDefaultExecutor());
}
/**
* Creates an input stream, which reads from the specified input
* and uses the provided executor instead of the default executor.
* @param in
* @param executor
*/
public SnzMTInputStream(InputStream in, Executor executor) {
super(in);
this.executor = executor;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
init();
if (meEof) {
return -1;
}
if (dbuffer == null || dbufferIndex >= dbuffer.getLength()) {
if(tasks.size() == 0) {
meEof = true;
return -1;
}
DecompTask task = tasks.removeFirst();
try {
dbuffer = task.get();
}
catch(Exception e) {
throw new IOException(e.getMessage());
}
dbufferIndex = 0;
newChunk();
}
if(len > dbuffer.getLength() - dbufferIndex) {
len = dbuffer.getLength() - dbufferIndex;
}
System.arraycopy(dbuffer.getData(), dbufferIndex, b, off, len);
dbufferIndex += len;
return len;
}
/**
* Returns the default executor or null if no default executor has
* been created yet.
* @return the default executor
*/
public static Executor getDefaultExecutor() {
return defaultExecutor;
}
/**
*
* Sets a new default executor.
*
*
*
* Setting a new default executor will have no effect on already created
* instances of this class. Setting the default executor to null will cause
* a new default executor to be created when needed.
*
*
*
* Note that replacing a running executor will not cause the previous default
* executor to be automatically shut down.
*
* @param executor a new default executor
*/
public static synchronized void setDefaultExecutor(Executor executor) {
defaultExecutor = executor;
}
private static synchronized Executor initDefaultExecutor() {
if(defaultExecutor == null) {
defaultExecutor =
new ThreadPoolExecutor(AVAILABLE_CPUS, AVAILABLE_CPUS * 2, 5, TimeUnit.SECONDS, new LinkedBlockingQueue(),
new ThreadFactory() {
private int cnt = 1;
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "SnzMTInputStream-" + (cnt++));
t.setDaemon(true);
return t;
}
});
}
return defaultExecutor;
}
private void newChunk() throws IOException {
if(!delegateEof) {
int cLength = readVInt();
if (cLength == 0) {
delegateEof = true;
return;
}
byte[] cbuffer = new byte[cLength];
int o = 0;
while(o < cLength) {
o += in.read(cbuffer, o, cbuffer.length - o);
}
DecompTask task = new DecompTask(cbuffer);
tasks.add(task);
executor.execute(task);
}
}
void init() throws IOException {
if(!initialized) {
super.init();
for(int i=0; i {
public DecompTask(byte[] source) {
this(source, new Buffer());
}
private DecompTask(final byte[] source, final Buffer result) {
super(new Runnable() {
public void run() {
SnappyDecompressor.decompress(source, result);
}
}, result);
}
}
}