Author: toad
Date: 2005-11-07 23:51:07 +0000 (Mon, 07 Nov 2005)
New Revision: 7495

Added:
   trunk/freenet/src/freenet/client/Compressor.java
   trunk/freenet/src/freenet/client/FileInserter.java
   trunk/freenet/src/freenet/client/GzipCompressor.java
   trunk/freenet/src/freenet/client/InserterContext.java
   trunk/freenet/src/freenet/client/InserterException.java
Modified:
   trunk/freenet/src/freenet/client/ClientMetadata.java
   trunk/freenet/src/freenet/client/FetchException.java
   trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
   trunk/freenet/src/freenet/client/InsertBlock.java
   trunk/freenet/src/freenet/client/Metadata.java
   trunk/freenet/src/freenet/keys/CHKBlock.java
   trunk/freenet/src/freenet/keys/CHKEncodeException.java
   trunk/freenet/src/freenet/keys/ClientCHKBlock.java
   trunk/freenet/src/freenet/keys/FreenetURI.java
Log:
Lots of work on inserts inc. splitfiles & compression.

Modified: trunk/freenet/src/freenet/client/ClientMetadata.java
===================================================================
--- trunk/freenet/src/freenet/client/ClientMetadata.java        2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/ClientMetadata.java        2005-11-07 
23:51:07 UTC (rev 7495)
@@ -33,4 +33,8 @@
                if(mimeType == null || mimeType.equals(""))
                        mimeType = clientMetadata.mimeType;
        }
+
+       public boolean isTrivial() {
+               return (mimeType == null || 
mimeType.equals(DefaultMIMETypes.DEFAULT_MIME_TYPE));
+       }
 }

Added: trunk/freenet/src/freenet/client/Compressor.java
===================================================================
--- trunk/freenet/src/freenet/client/Compressor.java    2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/client/Compressor.java    2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -0,0 +1,22 @@
+package freenet.client;
+
+import java.io.IOException;
+
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+
+/**
+ * A data compressor. Contains methods to get all data compressors.
+ * This is for single-file compression (gzip, bzip2) as opposed to archives.
+ */
+public abstract class Compressor {
+
+       public static Compressor gzip = new GzipCompressor();
+
+       public abstract Bucket compress(Bucket data, BucketFactory bf) throws 
IOException;
+
+       public int codecNumberForMetadata() {
+               return Metadata.COMPRESS_GZIP;
+       }
+
+}

Modified: trunk/freenet/src/freenet/client/FetchException.java
===================================================================
--- trunk/freenet/src/freenet/client/FetchException.java        2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/FetchException.java        2005-11-07 
23:51:07 UTC (rev 7495)
@@ -60,7 +60,7 @@
        public static final int NOT_IN_ARCHIVE = 10;
        /** Has more metastrings, can't fulfill them */
        public static final int HAS_MORE_METASTRINGS = 11;
-       /** Internal error, probably failed to read from a bucket */
+       /** Failed to read from or write to a bucket; a kind of internal error 
*/
        public static final int BUCKET_ERROR = 12;
        /** Data not found */
        public static final int DATA_NOT_FOUND = 13;

Added: trunk/freenet/src/freenet/client/FileInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/FileInserter.java  2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/client/FileInserter.java  2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -0,0 +1,142 @@
+package freenet.client;
+
+import java.io.IOException;
+
+import freenet.keys.CHKEncodeException;
+import freenet.keys.ClientCHKBlock;
+import freenet.keys.FreenetURI;
+import freenet.keys.NodeCHK;
+import freenet.node.LowLevelPutException;
+import freenet.support.Bucket;
+import freenet.support.BucketTools;
+
+/**
+ * Class that does high-level inserts.
+ */
+public class FileInserter {
+
+       InserterContext ctx;
+
+       public FileInserter(InserterContext context) {
+               this.ctx = context;
+       }
+
+       /**
+        * Do an insert.
+        * @param block The data to insert.
+        * @return The URI of the inserted data.
+        * @throws InserterException 
+        */
+       public FreenetURI run(InsertBlock block, boolean metadata) throws 
InserterException {
+               if(!block.desiredURI.toString(false).equals("CHK@"))
+                       throw new 
InserterException(InserterException.INVALID_URI);
+               
+               // Insert the content.
+               // If we have reason to create a metadata document, include the 
client metadata.
+               // Otherwise only create one (a redirect) with the client 
metadata, if there is any.
+               
+               // First, can it fit into a single block?
+               
+               Bucket data = block.data;
+               ClientCHKBlock chk;
+               
+               int compressionCodec = -1; // no compression
+
+               int bestCompressionCodec = -1; // no compression
+               Bucket bestCompressedData;
+               
+               if(data.size() > NodeCHK.BLOCK_SIZE && (!ctx.dontCompress)) {
+                       // Try to compress the data.
+                       // Try each algorithm, starting with the fastest and 
weakest.
+                       // Stop when run out of algorithms, or the compressed 
data fits in a single block.
+                       int algos = Metadata.countCompressAlgorithms();
+                       for(int i=0;i<algos;i++) {
+                               Compressor comp = 
Metadata.getCompressionAlgorithmByDifficulty(i);
+                               Bucket result = comp.compress(data, ctx.bf);
+                               if(result.size() < NodeCHK.BLOCK_SIZE) {
+                                       compressionCodec = -1;
+                                       data = result;
+                                       if(bestCompressedData != null)
+                                               
ctx.bf.freeBucket(bestCompressedData);
+                                       break;
+                               }
+                               if(bestCompressedData != null && result.size() 
<  bestCompressedData.size()) {
+                                       ctx.bf.freeBucket(bestCompressedData);
+                                       bestCompressedData = result;
+                                       bestCompressionCodec = 
comp.codecNumberForMetadata();
+                               } else if(bestCompressedData == null && 
result.size() < data.size()) {
+                                       bestCompressedData = result;
+                                       bestCompressionCodec = 
comp.codecNumberForMetadata();
+                               }
+                       }
+                       if(compressionCodec == -1) {
+                               compressionCodec = bestCompressionCodec;
+                               if(compressionCodec != -1) {
+                                       data = bestCompressedData;
+                               }
+                       }
+               }
+               
+               if(data.size() <= NodeCHK.BLOCK_SIZE) {
+                       if(compressionCodec == -1) {
+                               chk = 
ClientCHKBlock.encode(BucketTools.toByteArray(data), metadata, true);
+                       }
+               }
+               
+               if(data.size() <= NodeCHK.BLOCK_SIZE ||
+                               data.size() <= 
ClientCHKBlock.MAX_LENGTH_BEFORE_COMPRESSION) {
+                       try {
+                               chk = 
ClientCHKBlock.encode(BucketTools.toByteArray(data), metadata);
+                               return simplePutCHK(chk, block.clientMetadata);
+                       } catch (CHKEncodeException e) {
+                               // Too big! Encode to a splitfile, below.
+                       } catch (IOException e) {
+                               throw new 
InserterException(InserterException.BUCKET_ERROR);
+                       }
+               }
+               
+               // Too big, encode to a splitfile
+               SplitInserter splitInsert = new SplitInserter(data, 
block.clientMetadata);
+               splitInsert.run();
+       }
+
+       /**
+        * Simple insert. Only complication is that it might have some client 
metadata.
+        * @param chk The data encoded into a single CHK.
+        * @param clientMetadata The client metadata. If this is non-trivial, 
we will have to
+        * create a redirect document just to put the metadata in.
+        * @return The URI of the resulting CHK.
+        */
+       private FreenetURI simplePutCHK(ClientCHKBlock chk, ClientMetadata 
clientMetadata) {
+               try {
+                       ctx.client.putCHK(chk);
+               } catch (LowLevelPutException e) {
+                       translateException(e);
+               }
+               
+               if(clientMetadata == null || clientMetadata.isTrivial())
+                       // Don't need a redirect for the metadata
+                       return chk.getClientKey().getURI();
+               else {
+                       // Do need a redirect for the metadata
+                       Metadata metadata = new 
Metadata(Metadata.SIMPLE_REDIRECT, chk.getClientKey().getURI(), clientMetadata);
+                       return putMetadataCHK(metadata);
+               }
+       }
+
+       /** Put a metadata CHK 
+        * @throws InserterException If the insert fails.
+        */
+       private FreenetURI putMetadataCHK(Metadata metadata) throws 
InserterException {
+               byte[] data = metadata.writeToByteArray();
+               Bucket bucket;
+               try {
+                       bucket = BucketTools.makeImmutableBucket(ctx.bf, data);
+               } catch (IOException e) {
+                       throw new 
InserterException(InserterException.BUCKET_ERROR);
+               }
+               InsertBlock block = new InsertBlock(bucket, null, 
FreenetURI.EMPTY_CHK_URI);
+               return run(block, true);
+       }
+
+}

Added: trunk/freenet/src/freenet/client/GzipCompressor.java
===================================================================
--- trunk/freenet/src/freenet/client/GzipCompressor.java        2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/GzipCompressor.java        2005-11-07 
23:51:07 UTC (rev 7495)
@@ -0,0 +1,29 @@
+package freenet.client;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+
+public class GzipCompressor extends Compressor {
+
+       public Bucket compress(Bucket data, BucketFactory bf) throws 
IOException {
+               Bucket output = bf.makeBucket(-1);
+               InputStream is = data.getInputStream();
+               OutputStream os = output.getOutputStream();
+               GZIPOutputStream gos = new GZIPOutputStream(os);
+               byte[] buffer = new byte[4096];
+               while(true) {
+                       int x = is.read(buffer);
+                       if(x <= -1) break;
+                       if(x == 0) throw new IOException("Returned zero from 
read()");
+                       gos.write(buffer, 0, x);
+               }
+               gos.close();
+               return output;
+       }
+
+}

Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2005-11-07 20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2005-11-07 23:51:07 UTC (rev 7495)
@@ -65,8 +65,9 @@
        }

        public FreenetURI insert(InsertBlock insert) {
-               // TODO Auto-generated method stub
-               return null;
+               InserterContext context = new InserterContext(client);
+               FileInserter i = new FileInserter(context);
+               return i.run(insert);
        }

 }

Modified: trunk/freenet/src/freenet/client/InsertBlock.java
===================================================================
--- trunk/freenet/src/freenet/client/InsertBlock.java   2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/client/InsertBlock.java   2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -1,5 +1,6 @@
 package freenet.client;

+import freenet.keys.FreenetURI;
 import freenet.support.Bucket;

 /**
@@ -7,12 +8,14 @@
  */
 public class InsertBlock {

-       private Bucket data;
-       private String mimeType;
+       final Bucket data;
+       final FreenetURI desiredURI;
+       final ClientMetadata clientMetadata;

-       public InsertBlock(Bucket data, String mimeType) {
+       public InsertBlock(Bucket data, ClientMetadata metadata, FreenetURI 
desiredURI) {
                this.data = data;
-               this.mimeType = mimeType;
+               clientMetadata = metadata;
+               this.desiredURI = desiredURI;
        }

 }

Added: trunk/freenet/src/freenet/client/InserterContext.java
===================================================================
--- trunk/freenet/src/freenet/client/InserterContext.java       2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/InserterContext.java       2005-11-07 
23:51:07 UTC (rev 7495)
@@ -0,0 +1,20 @@
+package freenet.client;
+
+import freenet.node.SimpleLowLevelClient;
+import freenet.support.BucketFactory;
+
+/** Context object for an insert operation, including both simple and 
multi-file inserts */
+public class InserterContext {
+
+       final SimpleLowLevelClient client;
+       final BucketFactory bf;
+       /** If true, don't try to compress the data */
+       final boolean dontCompress;
+       
+       public InserterContext(SimpleLowLevelClient client, BucketFactory bf) {
+               this.client = client;
+               this.bf = bf;
+               dontCompress = false;
+       }
+
+}

Added: trunk/freenet/src/freenet/client/InserterException.java
===================================================================
--- trunk/freenet/src/freenet/client/InserterException.java     2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/client/InserterException.java     2005-11-07 
23:51:07 UTC (rev 7495)
@@ -0,0 +1,29 @@
+package freenet.client;
+
+import java.io.IOException;
+
+public class InserterException extends Exception {
+       private static final long serialVersionUID = -1106716067841151962L;
+       
+       final int mode;
+       
+       /** Get the failure mode. */
+       public int getMode() {
+               return mode;
+       }
+       
+       public InserterException(int m) {
+               mode = m;
+       }
+
+       public InserterException(int mode, IOException e) {
+               this.mode = mode;
+               initCause(e);
+       }
+
+       /** Caller supplied a URI we cannot use */
+       public static final int INVALID_URI = 1;
+       /** Failed to read from or write to a bucket; a kind of internal error 
*/
+       public static final int BUCKET_ERROR = 2;
+
+}

Modified: trunk/freenet/src/freenet/client/Metadata.java
===================================================================
--- trunk/freenet/src/freenet/client/Metadata.java      2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/client/Metadata.java      2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -277,6 +277,22 @@
        }

        /**
+        * Create another kind of simple Metadata object (a redirect or similar 
object).
+        * @param docType The document type.
+        * @param uri The URI pointed to.
+        * @param cm The client metadata, if any.
+        */
+       public Metadata(byte docType, FreenetURI uri, ClientMetadata cm) {
+               if(docType == SIMPLE_REDIRECT) {
+                       documentType = docType;
+                       clientMetadata = cm;
+                       setMIMEType(cm.getMIMEType());
+                       simpleRedirectKey = uri;
+               } else
+                       throw new IllegalArgumentException();
+       }
+
+       /**
         * Set the MIME type to a string. Compresses it if possible for transit.
         */
        private void setMIMEType(String type) {
@@ -626,4 +642,17 @@
        public FreenetURI[] getSplitfileCheckKeys() {
                return splitfileCheckKeys;
        }
+
+       /** Count the number of distinct compression algorithms currently 
supported. */
+       public static int countCompressAlgorithms() {
+               // FIXME we presently only support gzip. This should change in 
future.
+               return 1;
+       }
+
+       public static Compressor getCompressionAlgorithmByDifficulty(int i) {
+               if(i == 0)
+                       return Compressor.gzip;
+               // FIXME when we get more compression algos, put them here.
+               return null;
+       }
 }

Modified: trunk/freenet/src/freenet/keys/CHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKBlock.java        2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/keys/CHKBlock.java        2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -24,7 +24,7 @@
     final byte[] header;
     final short hashIdentifier;
     final NodeCHK chk;
-    protected static final int MAX_LENGTH_BEFORE_COMPRESSION = 1024 * 1024;
+    public static final int MAX_LENGTH_BEFORE_COMPRESSION = 1024 * 1024;
     final static int HASH_SHA1 = 1;

     public String toString() {

Modified: trunk/freenet/src/freenet/keys/CHKEncodeException.java
===================================================================
--- trunk/freenet/src/freenet/keys/CHKEncodeException.java      2005-11-07 
20:17:23 UTC (rev 7494)
+++ trunk/freenet/src/freenet/keys/CHKEncodeException.java      2005-11-07 
23:51:07 UTC (rev 7495)
@@ -4,6 +4,7 @@
  * @author amphibian
  * 
  * Exception thrown when a CHK encoding fails.
+ * Specifically, it is thrown when the data is too big to encode.
  */
 public class CHKEncodeException extends Exception {
        static final long serialVersionUID = -1;

Modified: trunk/freenet/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/keys/ClientCHKBlock.java  2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -47,9 +47,12 @@

     /**
      * Encode a block of data to a CHKBlock.
+     * @param sourceData The data to encode.
+     * @param asMetadata Is this a metadata key?
+     * @param dontCompress If set, don't even try to compress.
      */

-    static public ClientCHKBlock encode(byte[] sourceData) throws 
CHKEncodeException {
+    static public ClientCHKBlock encode(byte[] sourceData, boolean asMetadata, 
boolean dontCompress) throws CHKEncodeException {
         byte[] data;
         byte[] header;
         ClientCHK key;
@@ -58,7 +61,7 @@
         boolean compressed = false;
         if(sourceData.length > MAX_LENGTH_BEFORE_COMPRESSION)
             throw new CHKEncodeException("Too big");
-        if(sourceData.length != 0) {
+        if(sourceData.length > NodeCHK.BLOCK_SIZE && !dontCompress) {
             int sourceLength = sourceData.length;
             byte[] cbuf = new byte[32768+1024];
             Deflater compressor = new Deflater();
@@ -149,7 +152,7 @@
         byte[] finalHash = md160.digest(data);

         // Now convert it into a ClientCHK
-        key = new ClientCHK(finalHash, encKey, compressed, false, 
ClientCHK.ALGO_AES_PCFB_256);
+        key = new ClientCHK(finalHash, encKey, compressed, asMetadata, 
ClientCHK.ALGO_AES_PCFB_256);

         try {
             return new ClientCHKBlock(data, header, key, false);

Modified: trunk/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- trunk/freenet/src/freenet/keys/FreenetURI.java      2005-11-07 20:17:23 UTC 
(rev 7494)
+++ trunk/freenet/src/freenet/keys/FreenetURI.java      2005-11-07 23:51:07 UTC 
(rev 7495)
@@ -86,6 +86,8 @@
                this(keyType, docName, (String[]) null, null, null, null);
        }

+       public static final FreenetURI EMPTY_CHK_URI = new FreenetURI("CHK", 
null, null, null, null, null);
+       
        public FreenetURI(
                String keyType,
                String docName,


Reply via email to