forked from I2P_Developers/i2p.i2p
* 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
|
private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send
|
||||||
/** should we gzip each payload prior to sending it? */
|
/** should we gzip each payload prior to sending it? */
|
||||||
private final static boolean SHOULD_COMPRESS = true;
|
private final static boolean SHOULD_COMPRESS = true;
|
||||||
|
private final static boolean SHOULD_DECOMPRESS = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session, reading the Destination, PrivateKey, and SigningPrivateKey
|
* 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.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.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.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() {
|
protected long getTimeout() {
|
||||||
@ -75,6 +78,27 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
clearStates();
|
clearStates();
|
||||||
super.destroySession(sendDisconnect);
|
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
|
@Override
|
||||||
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
|
public boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException {
|
||||||
@ -92,8 +116,30 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
throws I2PSessionException {
|
throws I2PSessionException {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
|
||||||
if (isClosed()) throw new I2PSessionException("Already closed");
|
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);
|
return sendBestEffort(dest, payload, keyUsed, tagsSent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +153,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
|
|||||||
_log.error("Error: message " + msgId + " already received!");
|
_log.error("Error: message " + msgId + " already received!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (SHOULD_COMPRESS) {
|
// future - check magic number to see whether to decompress
|
||||||
|
if (SHOULD_DECOMPRESS) {
|
||||||
try {
|
try {
|
||||||
return DataHelper.decompress(compressed);
|
return DataHelper.decompress(compressed);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
@ -33,6 +33,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
import java.util.zip.Deflater;
|
||||||
|
|
||||||
import net.i2p.util.ByteCache;
|
import net.i2p.util.ByteCache;
|
||||||
import net.i2p.util.OrderedProperties;
|
import net.i2p.util.OrderedProperties;
|
||||||
@ -852,15 +853,21 @@ public class DataHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final int MAX_UNCOMPRESSED = 40*1024;
|
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 */
|
/** compress the data and return a new GZIP compressed array */
|
||||||
public static byte[] compress(byte orig[]) {
|
public static byte[] compress(byte orig[]) {
|
||||||
return compress(orig, 0, orig.length);
|
return compress(orig, 0, orig.length);
|
||||||
}
|
}
|
||||||
public static byte[] compress(byte orig[], int offset, int size) {
|
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 ((orig == null) || (orig.length <= 0)) return orig;
|
||||||
if (size >= MAX_UNCOMPRESSED)
|
if (size >= MAX_UNCOMPRESSED)
|
||||||
throw new IllegalArgumentException("tell jrandom size=" + size);
|
throw new IllegalArgumentException("tell jrandom size=" + size);
|
||||||
ReusableGZIPOutputStream out = ReusableGZIPOutputStream.acquire();
|
ReusableGZIPOutputStream out = ReusableGZIPOutputStream.acquire();
|
||||||
|
out.setLevel(level);
|
||||||
try {
|
try {
|
||||||
out.write(orig, offset, size);
|
out.write(orig, offset, size);
|
||||||
out.finish();
|
out.finish();
|
||||||
|
@ -3,6 +3,7 @@ package net.i2p.util;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
@ -51,6 +52,10 @@ public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
|
|||||||
public void reset() {
|
public void reset() {
|
||||||
super.reset();
|
super.reset();
|
||||||
_buffer.reset();
|
_buffer.reset();
|
||||||
|
def.setLevel(Deflater.BEST_COMPRESSION);
|
||||||
|
}
|
||||||
|
public void setLevel(int level) {
|
||||||
|
def.setLevel(level);
|
||||||
}
|
}
|
||||||
/** pull the contents of the stream written */
|
/** pull the contents of the stream written */
|
||||||
public byte[] getData() { return _buffer.toByteArray(); }
|
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
|
2008-11-13 zzz
|
||||||
* Streaming:
|
* Streaming:
|
||||||
- Add more info to Connection.toString() for debugging
|
- Add more info to Connection.toString() for debugging
|
||||||
|
@ -17,7 +17,7 @@ import net.i2p.CoreVersion;
|
|||||||
public class RouterVersion {
|
public class RouterVersion {
|
||||||
public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $";
|
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 String VERSION = "0.6.4";
|
||||||
public final static long BUILD = 10;
|
public final static long BUILD = 11;
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||||
System.out.println("Router ID: " + RouterVersion.ID);
|
System.out.println("Router ID: " + RouterVersion.ID);
|
||||||
|
Reference in New Issue
Block a user