* I2CP Compression:
- Add i2cp.gzip option (default true) - Don't bother compressing if really small
This commit is contained in:
@ -39,6 +39,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send
|
||||
/** should we gzip each payload prior to sending it? */
|
||||
private final static boolean SHOULD_COMPRESS = true;
|
||||
private final static boolean SHOULD_DECOMPRESS = true;
|
||||
|
||||
/**
|
||||
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
||||
@ -64,6 +65,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.receiveStatusTime", "How long it took to get any status", "i2cp", new long[] { 60*1000, 10*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 60*1000, 30*60*1000 });
|
||||
}
|
||||
|
||||
protected long getTimeout() {
|
||||
@ -75,6 +78,27 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
clearStates();
|
||||
super.destroySession(sendDisconnect);
|
||||
}
|
||||
|
||||
/** Don't bother if really small.
|
||||
* Three 66-byte messages will fit in one tunnel message.
|
||||
* Four messages don't fit no matter how small. So below 66 it isn't worth it.
|
||||
* See ConnectionOptions.java in the streaming lib for similar calculations.
|
||||
* Since we still have to pass it through gzip -0 the CPU savings
|
||||
* is trivial but it's the best we can do for now. See below.
|
||||
* i2cp.gzip defaults to SHOULD_COMPRESS = true.
|
||||
* Perhaps the http server (which does its own compression)
|
||||
* and P2P apps (with generally uncompressible data) should
|
||||
* set to false.
|
||||
*/
|
||||
private static final int DONT_COMPRESS_SIZE = 66;
|
||||
private boolean shouldCompress(int size) {
|
||||
if (size <= DONT_COMPRESS_SIZE)
|
||||
return false;
|
||||
String p = getOptions().getProperty("i2cp.gzip");
|
||||
if (p != null)
|
||||
return Boolean.valueOf(p).booleanValue();
|
||||
return SHOULD_COMPRESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
|
||||
@ -92,8 +116,30 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
throws I2PSessionException {
|
||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
|
||||
if (isClosed()) throw new I2PSessionException("Already closed");
|
||||
if (SHOULD_COMPRESS) payload = DataHelper.compress(payload, offset, size);
|
||||
else throw new IllegalStateException("we need to update sendGuaranteed to support partial send");
|
||||
|
||||
// Sadly there is no way to send something completely uncompressed in a backward-compatible way,
|
||||
// so we have to still send it in a gzip format, which adds 23 bytes (2.4% for a 960-byte msg)
|
||||
// (10 byte header + 5 byte block header + 8 byte trailer)
|
||||
// In the future we can add a one-byte magic number != 0x1F to signal an uncompressed msg
|
||||
// (Gzip streams start with 0x1F 0x8B 0x08)
|
||||
// assuming we don't need the CRC-32 that comes with gzip (do we?)
|
||||
// Maybe implement this soon in receiveMessage() below so we are ready
|
||||
// in case we ever make an incompatible network change.
|
||||
// This would save 22 of the 23 bytes and a little CPU.
|
||||
boolean sc = shouldCompress(size);
|
||||
if (sc)
|
||||
payload = DataHelper.compress(payload, offset, size);
|
||||
else
|
||||
payload = DataHelper.compress(payload, offset, size, DataHelper.NO_COMPRESSION);
|
||||
//else throw new IllegalStateException("we need to update sendGuaranteed to support partial send");
|
||||
|
||||
int compressed = payload.length;
|
||||
if (_log.shouldLog(Log.INFO)) {
|
||||
String d = dest.calculateHash().toBase64().substring(0,4);
|
||||
_log.info("sending message to: " + d + " compress? " + sc + " sizeIn=" + size + " sizeOut=" + compressed);
|
||||
}
|
||||
_context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
|
||||
_context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
|
||||
return sendBestEffort(dest, payload, keyUsed, tagsSent);
|
||||
}
|
||||
|
||||
@ -107,7 +153,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
||||
_log.error("Error: message " + msgId + " already received!");
|
||||
return null;
|
||||
}
|
||||
if (SHOULD_COMPRESS) {
|
||||
// future - check magic number to see whether to decompress
|
||||
if (SHOULD_DECOMPRESS) {
|
||||
try {
|
||||
return DataHelper.decompress(compressed);
|
||||
} catch (IOException ioe) {
|
||||
|
@ -33,6 +33,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
@ -852,15 +853,21 @@ public class DataHelper {
|
||||
}
|
||||
|
||||
private static final int MAX_UNCOMPRESSED = 40*1024;
|
||||
public static final int MAX_COMPRESSION = Deflater.BEST_COMPRESSION;
|
||||
public static final int NO_COMPRESSION = Deflater.NO_COMPRESSION;
|
||||
/** compress the data and return a new GZIP compressed array */
|
||||
public static byte[] compress(byte orig[]) {
|
||||
return compress(orig, 0, orig.length);
|
||||
}
|
||||
public static byte[] compress(byte orig[], int offset, int size) {
|
||||
return compress(orig, offset, size, MAX_COMPRESSION);
|
||||
}
|
||||
public static byte[] compress(byte orig[], int offset, int size, int level) {
|
||||
if ((orig == null) || (orig.length <= 0)) return orig;
|
||||
if (size >= MAX_UNCOMPRESSED)
|
||||
throw new IllegalArgumentException("tell jrandom size=" + size);
|
||||
ReusableGZIPOutputStream out = ReusableGZIPOutputStream.acquire();
|
||||
out.setLevel(level);
|
||||
try {
|
||||
out.write(orig, offset, size);
|
||||
out.finish();
|
||||
|
@ -3,6 +3,7 @@ package net.i2p.util;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -51,6 +52,10 @@ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
|
||||
public void reset() {
|
||||
super.reset();
|
||||
_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(); }
|
||||
|
10
history.txt
10
history.txt
@ -1,3 +1,13 @@
|
||||
2008-11-15 zzz
|
||||
* Build files:
|
||||
- Don't die if depend not available
|
||||
- Only verify Jetty hash once
|
||||
- Add streaming lib tests to depend task
|
||||
* I2CP Compression:
|
||||
- Add i2cp.gzip option (default true)
|
||||
- Add compression stats
|
||||
- Don't bother compressing if really small
|
||||
|
||||
2008-11-13 zzz
|
||||
* Streaming:
|
||||
- Add more info to Connection.toString() for debugging
|
||||
|
@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
|
||||
public final static String VERSION = "0.6.4";
|
||||
public final static long BUILD = 10;
|
||||
public final static long BUILD = 11;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
Reference in New Issue
Block a user