* i2ptunnel HTTPResponseOutputStream: Use reusable gunzipper

and a larger pipe for efficiency
This commit is contained in:
zzz
2011-09-19 14:13:24 +00:00
parent 49eeed6ac8
commit f186076fb0
7 changed files with 153 additions and 92 deletions

View File

@@ -0,0 +1,44 @@
package net.i2p.util;
import java.io.PipedInputStream;
/**
* Java 1.5 PipedInputStream buffers are only 1024 bytes; our I2CP messages are typically 1730 bytes,
* thus causing thread blockage before the whole message is transferred.
* We can specify buffer size in 1.6 but not in 1.5.
*
* Until we switch to Java 1.6 -
* http://javatechniques.com/blog/low-memory-deep-copy-technique-for-java-objects/
*
* Moved from InternalServerSocket.
* @since 0.8.9
*/
public class BigPipedInputStream extends PipedInputStream {
private static final boolean oneDotSix =
(new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0;
private static final int PIPE_SIZE = 64*1024;
private BigPipedInputStream(int size) {
super();
buffer = new byte[size];
}
/** default size 64K */
public static PipedInputStream getInstance() {
return getInstance(PIPE_SIZE);
}
public static PipedInputStream getInstance(int size) {
if (oneDotSix) {
try {
return new PipedInputStream(size);
} catch (Throwable t) {
// NoSuchMethodException or NoSuchMethodError if we somehow got the
// version detection wrong or the JVM doesn't support it
}
}
return new BigPipedInputStream(size);
}
}

View File

@@ -99,8 +99,8 @@ public class InternalServerSocket extends ServerSocket {
InternalServerSocket iss = _sockets.get(Integer.valueOf(port));
if (iss == null)
throw new IOException("No server for port: " + port);
PipedInputStream cis = new BigPipedInputStream();
PipedInputStream sis = new BigPipedInputStream();
PipedInputStream cis = BigPipedInputStream.getInstance();
PipedInputStream sis = BigPipedInputStream.getInstance();
PipedOutputStream cos = new PipedOutputStream(sis);
PipedOutputStream sos = new PipedOutputStream(cis);
clientSock.setInputStream(cis);
@@ -108,18 +108,6 @@ public class InternalServerSocket extends ServerSocket {
iss.queueConnection(new InternalSocket(sis, sos));
}
/**
* Until we switch to Java 1.6
* http://javatechniques.com/blog/low-memory-deep-copy-technique-for-java-objects/
*/
private static class BigPipedInputStream extends PipedInputStream {
protected static int PIPE_SIZE = 64*1024;
public BigPipedInputStream() {
super();
buffer = new byte[PIPE_SIZE];
}
}
private void queueConnection(InternalSocket sock) throws IOException {
if (!_running)
throw new IOException("Server closed for port: " + _port);

View File

@@ -14,17 +14,17 @@ import java.util.Arrays;
*/
public class LookaheadInputStream extends FilterInputStream {
private boolean _eofReached;
private byte[] _footerLookahead;
private final byte[] _footerLookahead;
private static final InputStream _fakeInputStream = new ByteArrayInputStream(new byte[0]);
public LookaheadInputStream(int lookaheadSize) {
super(_fakeInputStream);
_eofReached = false;
_footerLookahead = new byte[lookaheadSize];
}
public boolean getEOFReached() { return _eofReached; }
/** blocking! */
public void initialize(InputStream src) throws IOException {
in = src;
_eofReached = false;
@@ -35,7 +35,6 @@ public class LookaheadInputStream extends FilterInputStream {
if (read == -1) throw new IOException("EOF reading the footer lookahead");
footerRead += read;
}
boolean f = true;
}
@Override
@@ -53,10 +52,12 @@ public class LookaheadInputStream extends FilterInputStream {
if (rv < 0) rv += 256;
return rv;
}
@Override
public int read(byte buf[]) throws IOException {
return read(buf, 0, buf.length);
}
@Override
public int read(byte buf[], int off, int len) throws IOException {
if (_eofReached)

View File

@@ -20,9 +20,9 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
private static final int FOOTER_SIZE = 8; // CRC32 + ISIZE
private static final boolean DEBUG = false;
/** keep a typesafe copy of (LookaheadInputStream)in */
private LookaheadInputStream _lookaheadStream;
private CRC32 _crc32;
private byte _buf1[] = new byte[1];
private final LookaheadInputStream _lookaheadStream;
private final CRC32 _crc32;
private final byte _buf1[] = new byte[1];
private boolean _complete;
/**
@@ -34,8 +34,11 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
super(new LookaheadInputStream(FOOTER_SIZE), new Inflater(true));
_lookaheadStream = (LookaheadInputStream)in;
_crc32 = new CRC32();
_complete = false;
}
/**
* Warning - blocking!
*/
public ResettableGZIPInputStream(InputStream compressedStream) throws IOException {
this();
initialize(compressedStream);
@@ -78,6 +81,7 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
public int read(byte buf[]) throws IOException {
return read(buf, 0, buf.length);
}
@Override
public int read(byte buf[], int off, int len) throws IOException {
if (_complete) {
@@ -100,9 +104,69 @@ public class ResettableGZIPInputStream extends InflaterInputStream {
}
}
long getCurrentCRCVal() { return _crc32.getValue(); }
/**
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
* @since 0.8.9
*/
public long getTotalRead() {
try {
return inf.getBytesRead();
} catch (Exception e) {
return 0;
}
}
/**
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
* @since 0.8.9
*/
public long getTotalExpanded() {
try {
return inf.getBytesWritten();
} catch (Exception e) {
// possible NPE in some implementations
return 0;
}
}
/**
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
* @since 0.8.9
*/
public long getRemaining() {
try {
return inf.getRemaining();
} catch (Exception e) {
// possible NPE in some implementations
return 0;
}
}
/**
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
* @since 0.8.9
*/
public boolean getFinished() {
try {
return inf.finished();
} catch (Exception e) {
// possible NPE in some implementations
return true;
}
}
/**
* Moved from i2ptunnel HTTPResponseOutputStream.InternalGZIPInputStream
* @since 0.8.9
*/
@Override
public String toString() {
return "Read: " + getTotalRead() + " expanded: " + getTotalExpanded() + " remaining: " + getRemaining() + " finished: " + getFinished();
}
private long getCurrentCRCVal() { return _crc32.getValue(); }
void verifyFooter() throws IOException {
private void verifyFooter() throws IOException {
byte footer[] = _lookaheadStream.getFooter();
long expectedCRCVal = _crc32.getValue();

View File

@@ -26,14 +26,14 @@ public class ResettableGZIPOutputStream extends DeflaterOutputStream {
private boolean _headerWritten;
/** how much data is in the uncompressed stream? */
private long _writtenSize;
private CRC32 _crc32;
private final CRC32 _crc32;
private static final boolean DEBUG = false;
public ResettableGZIPOutputStream(OutputStream o) {
super(o, new Deflater(9, true));
_headerWritten = false;
_crc32 = new CRC32();
}
/**
* Reinitialze everything so we can write a brand new gzip output stream
* again.

View File

@@ -1,6 +1,6 @@
package net.i2p.util;
import java.io.ByteArrayInputStream;
//import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
@@ -9,9 +9,11 @@ import java.util.concurrent.LinkedBlockingQueue;
import net.i2p.data.DataHelper;
/**
* Provide a cache of reusable GZIP streams, each handling up to 32KB without
* Provide a cache of reusable GZIP streams, each handling up to 40 KB output without
* expansion.
*
* This compresses to memory only. Retrieve the compressed data with getData().
* There is no facility to compress to an output stream.
*/
public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
// Apache Harmony 5.0M13 Deflater doesn't work after reset()
@@ -50,10 +52,12 @@ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
}
private ByteArrayOutputStream _buffer = null;
private ReusableGZIPOutputStream() {
super(new ByteArrayOutputStream(40*1024));
_buffer = (ByteArrayOutputStream)out;
}
/** clear the data so we can start again afresh */
@Override
public void reset() {
@@ -61,9 +65,11 @@ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
_buffer.reset();
def.setLevel(Deflater.BEST_COMPRESSION);
}
public void setLevel(int level) {
def.setLevel(level);
}
/** pull the contents of the stream written */
public byte[] getData() { return _buffer.toByteArray(); }