diff --git a/router/java/src/net/i2p/data/i2np/GarlicClove.java b/router/java/src/net/i2p/data/i2np/GarlicClove.java
index 834412065e1d35de64ec537c624e4cc2d5254d12..3145284466aa297f40c591e2ff80c9335ad697d3 100644
--- a/router/java/src/net/i2p/data/i2np/GarlicClove.java
+++ b/router/java/src/net/i2p/data/i2np/GarlicClove.java
@@ -13,11 +13,11 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Date;
 
+import net.i2p.I2PAppContext;
 import net.i2p.data.Certificate;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.DataHelper;
 import net.i2p.data.DataStructureImpl;
-import net.i2p.router.RouterContext;
 import net.i2p.util.Log;
 
 /**
@@ -29,19 +29,17 @@ import net.i2p.util.Log;
  * @author jrandom
  */
 public class GarlicClove extends DataStructureImpl {
-    private final Log _log;
-    //private final RouterContext _context;
+    //private final Log _log;
+    private final I2PAppContext _context;
     private DeliveryInstructions _instructions;
     private I2NPMessage _msg;
     private long _cloveId;
     private Date _expiration;
     private Certificate _certificate;
-    private final I2NPMessageHandler _handler;
     
-    public GarlicClove(RouterContext context) {
-        //_context = context;
-        _log = context.logManager().getLog(GarlicClove.class);
-        _handler = new I2NPMessageHandler(context);
+    public GarlicClove(I2PAppContext context) {
+        _context = context;
+        //_log = context.logManager().getLog(GarlicClove.class);
         _cloveId = -1;
     }
     
@@ -58,8 +56,11 @@ public class GarlicClove extends DataStructureImpl {
     
     /**
      *  @deprecated unused, use byte array method to avoid copying
+     *  @throws UnsupportedOperationException always
      */
     public void readBytes(InputStream in) throws DataFormatException, IOException {
+        throw new UnsupportedOperationException();
+/****
         _instructions = new DeliveryInstructions();
         _instructions.readBytes(in);
         if (_log.shouldLog(Log.DEBUG))
@@ -78,17 +79,22 @@ public class GarlicClove extends DataStructureImpl {
         _certificate = Certificate.create(in);
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Read cert: " + _certificate);
+****/
     }
 
+    /**
+     *
+     */
     public int readBytes(byte source[], int offset) throws DataFormatException {
         int cur = offset;
         _instructions = new DeliveryInstructions();
         cur += _instructions.readBytes(source, cur);
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Read instructions: " + _instructions);
+        //if (_log.shouldLog(Log.DEBUG))
+        //    _log.debug("Read instructions: " + _instructions);
         try {
-            cur += _handler.readMessage(source, cur);
-            _msg = _handler.lastRead();
+            I2NPMessageHandler handler = new I2NPMessageHandler(_context);
+            cur += handler.readMessage(source, cur);
+            _msg = handler.lastRead();
         } catch (I2NPMessageException ime) {
             throw new DataFormatException("Unable to read the message from a garlic clove", ime);
         }
@@ -96,21 +102,24 @@ public class GarlicClove extends DataStructureImpl {
         cur += 4;
         _expiration = DataHelper.fromDate(source, cur);
         cur += DataHelper.DATE_LENGTH;
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
+        //if (_log.shouldLog(Log.DEBUG))
+        //    _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
         //_certificate = new Certificate();
         //cur += _certificate.readBytes(source, cur);
         _certificate = Certificate.create(source, cur);
         cur += _certificate.size();
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Read cert: " + _certificate);
+        //if (_log.shouldLog(Log.DEBUG))
+        //    _log.debug("Read cert: " + _certificate);
         return cur - offset;
     }
 
     /**
      *  @deprecated unused, use byte array method to avoid copying
+     *  @throws UnsupportedOperationException always
      */
     public void writeBytes(OutputStream out) throws DataFormatException, IOException {
+        throw new UnsupportedOperationException();
+/****
         StringBuilder error = null; 
         if (_instructions == null) {
             if (error == null) error = new StringBuilder();
@@ -158,15 +167,19 @@ public class GarlicClove extends DataStructureImpl {
         _certificate.writeBytes(out);
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("Written cert: " + _certificate);
+****/
     }
 
+    /**
+     *
+     */
     @Override
     public byte[] toByteArray() {
         byte rv[] = new byte[estimateSize()];
         int offset = 0;
         offset += _instructions.writeBytes(rv, offset);
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Wrote instructions: " + _instructions);
+        //if (_log.shouldLog(Log.DEBUG))
+        //    _log.debug("Wrote instructions: " + _instructions);
         //offset += _msg.toByteArray(rv);
         try {
             byte m[] = _msg.toByteArray();
@@ -178,8 +191,10 @@ public class GarlicClove extends DataStructureImpl {
         DataHelper.toDate(rv, offset, _expiration.getTime());
         offset += DataHelper.DATE_LENGTH;
         offset += _certificate.writeBytes(rv, offset);
-        if (offset != rv.length)
-            _log.log(Log.CRIT, "Clove offset: " + offset + " but estimated length: " + rv.length);
+        if (offset != rv.length) {
+            Log log = I2PAppContext.getGlobalContext().logManager().getLog(GarlicClove.class);
+            log.error("Clove offset: " + offset + " but estimated length: " + rv.length);
+        }
         return rv;
     }
     
@@ -196,31 +211,31 @@ public class GarlicClove extends DataStructureImpl {
         if ( (obj == null) || !(obj instanceof GarlicClove))
             return false;
         GarlicClove clove = (GarlicClove)obj;
-        return DataHelper.eq(getCertificate(), clove.getCertificate()) &&
-               _cloveId == clove.getCloveId() &&
-               DataHelper.eq(getData(), clove.getData()) &&
-               DataHelper.eq(getExpiration(), clove.getExpiration()) &&
-               DataHelper.eq(getInstructions(),  clove.getInstructions());
+        return DataHelper.eq(_certificate, clove._certificate) &&
+               _cloveId == clove._cloveId &&
+               DataHelper.eq(_msg, clove._msg) &&
+               DataHelper.eq(_expiration, clove._expiration) &&
+               DataHelper.eq(_instructions,  clove._instructions);
     }
     
     @Override
     public int hashCode() {
-        return DataHelper.hashCode(getCertificate()) +
-               (int)getCloveId() +
-               DataHelper.hashCode(getData()) +
-               DataHelper.hashCode(getExpiration()) +
-               DataHelper.hashCode(getInstructions());
+        return DataHelper.hashCode(_certificate) ^
+               (int) _cloveId ^
+               DataHelper.hashCode(_msg) ^
+               DataHelper.hashCode(_expiration) ^
+               DataHelper.hashCode(_instructions);
     }
     
     @Override
     public String toString() {
         StringBuilder buf = new StringBuilder(128);
         buf.append("[GarlicClove: ");
-        buf.append("\n\tInstructions: ").append(getInstructions());
-        buf.append("\n\tCertificate: ").append(getCertificate());
-        buf.append("\n\tClove ID: ").append(getCloveId());
-        buf.append("\n\tExpiration: ").append(getExpiration());
-        buf.append("\n\tData: ").append(getData());
+        buf.append("\n\tInstructions: ").append(_instructions);
+        buf.append("\n\tCertificate: ").append(_certificate);
+        buf.append("\n\tClove ID: ").append(_cloveId);
+        buf.append("\n\tExpiration: ").append(_expiration);
+        buf.append("\n\tData: ").append(_msg);
         buf.append("]");
         return buf.toString();
     }
diff --git a/router/java/src/net/i2p/router/message/CloveSet.java b/router/java/src/net/i2p/router/message/CloveSet.java
index caa5e1bff3bfb76b5e7a535afa2b5261915c700d..d03289931a165f719193098f0a7f9411192e5e54 100644
--- a/router/java/src/net/i2p/router/message/CloveSet.java
+++ b/router/java/src/net/i2p/router/message/CloveSet.java
@@ -8,45 +8,47 @@ package net.i2p.router.message;
  *
  */
 
-import java.util.ArrayList;
-import java.util.List;
-
 import net.i2p.data.Certificate;
 import net.i2p.data.i2np.GarlicClove;
 
 /**
- * Wrap up the data contained in a CloveMessage after being decrypted
+ * Wrap up the data contained in a GarlicMessage after being decrypted
  *
  */
 class CloveSet {
-    private final List<GarlicClove> _cloves;
-    private Certificate _cert;
-    private long _msgId;
-    private long _expiration;
+    private final GarlicClove[] _cloves;
+    private final Certificate _cert;
+    private final long _msgId;
+    private final long _expiration;
     
-    public CloveSet() {
-	_cloves = new ArrayList<GarlicClove>(4);
-	_msgId = -1;
-	_expiration = -1;
+    /**
+     *  @param cloves non-null, all entries non-null
+     *  @param cert non-null
+     */
+    public CloveSet(GarlicClove[] cloves, Certificate cert, long msgId, long expiration) {
+	_cloves = cloves;
+        _cert = cert;
+	_msgId = msgId;
+	_expiration = expiration;
     }
     
-    public int getCloveCount() { return _cloves.size(); }
-    public void addClove(GarlicClove clove) { _cloves.add(clove); }
-    public GarlicClove getClove(int index) { return _cloves.get(index); }
+    public int getCloveCount() { return _cloves.length; }
+
+    /** @throws AIOOBE */
+    public GarlicClove getClove(int index) { return _cloves[index]; }
     
     public Certificate getCertificate() { return _cert; }
-    public void setCertificate(Certificate cert) { _cert = cert; }
+
     public long getMessageId() { return _msgId; }
-    public void setMessageId(long id) { _msgId = id; }
+
     public long getExpiration() { return _expiration; }
-    public void setExpiration(long expiration) { _expiration = expiration; }
     
     @Override
     public String toString() { 
 	StringBuilder buf = new StringBuilder(128);
 	buf.append("{");
-	for (int i = 0; i < _cloves.size(); i++) {
-	    GarlicClove clove = _cloves.get(i);
+	for (int i = 0; i < _cloves.length; i++) {
+	    GarlicClove clove = _cloves[i];
 	    if (clove.getData() != null)
 		buf.append(clove.getData().getClass().getName()).append(", ");
 	    else
diff --git a/router/java/src/net/i2p/router/message/GarlicMessageParser.java b/router/java/src/net/i2p/router/message/GarlicMessageParser.java
index 083ffd8c3759968356d8d5b85460b93809ebc059..11b0d40c9ed69eb8513aa3a95a5997389610a68f 100644
--- a/router/java/src/net/i2p/router/message/GarlicMessageParser.java
+++ b/router/java/src/net/i2p/router/message/GarlicMessageParser.java
@@ -28,6 +28,12 @@ class GarlicMessageParser {
     private final Log _log;
     private final RouterContext _context;
     
+    /**
+     *  Huge limit just to reduce chance of trouble. Typ. usage is 3.
+     *  As of 0.9.12. Was 255.
+     */
+    private static final int MAX_CLOVES = 32;
+
     public GarlicMessageParser(RouterContext context) { 
         _context = context;
         _log = _context.logManager().getLog(GarlicMessageParser.class);
@@ -64,18 +70,19 @@ class GarlicMessageParser {
     private CloveSet readCloveSet(byte data[]) throws DataFormatException {
         int offset = 0;
         
-        CloveSet set = new CloveSet();
-
         int numCloves = (int)DataHelper.fromLong(data, offset, 1);
         offset++;
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("# cloves to read: " + numCloves);
+        if (numCloves <= 0 || numCloves > MAX_CLOVES)
+            throw new DataFormatException("bad clove count " + numCloves);
+        GarlicClove[] cloves = new GarlicClove[numCloves];
         for (int i = 0; i < numCloves; i++) {
             //if (_log.shouldLog(Log.DEBUG))
             //    _log.debug("Reading clove " + i);
                 GarlicClove clove = new GarlicClove(_context);
                 offset += clove.readBytes(data, offset);
-                set.addClove(clove);
+                cloves[i] = clove;
             //if (_log.shouldLog(Log.DEBUG))
             //    _log.debug("After reading clove " + i);
         }
@@ -85,11 +92,10 @@ class GarlicMessageParser {
         offset += cert.size();
         long msgId = DataHelper.fromLong(data, offset, 4);
         offset += 4;
-        Date expiration = DataHelper.fromDate(data, offset);
+        //Date expiration = DataHelper.fromDate(data, offset);
+        long expiration = DataHelper.fromLong(data, offset, 8);
 
-        set.setCertificate(cert);
-        set.setMessageId(msgId);
-        set.setExpiration(expiration.getTime());
+        CloveSet set = new CloveSet(cloves, cert, msgId, expiration);
         return set;
     }
 }