Optimization of TableEnumerator/MultiWrite

Change-Id: Iefc6cb15d6ee2a16b288a867499d79802320df09
diff --git a/src/main/java/edu/stanford/ramcloud/JRamCloud.java b/src/main/java/edu/stanford/ramcloud/JRamCloud.java
index dfb4b6a..63b0748 100644
--- a/src/main/java/edu/stanford/ramcloud/JRamCloud.java
+++ b/src/main/java/edu/stanford/ramcloud/JRamCloud.java
@@ -14,9 +14,8 @@
  */
 
 package edu.stanford.ramcloud;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.lang.Integer;
+import java.util.LinkedList;
 
 /*
  * This class provides Java bindings for RAMCloud. Right now it is a rather
@@ -78,31 +77,53 @@
         }	
     }
     
-    public static class multiReadObject {
-        long tableId;
-        byte[] key;
+    public static class MultiReadObject {
+        public long[] tableId;
+        public byte[] key[];
+	public short[] keyLength;
         
-        public multiReadObject(long _tableId, byte[] _key){
-            tableId = _tableId;
-            key = _key;
+        public MultiReadObject(int size){
+            this.tableId = new long[size];
+            this.key = new byte[size][];
+	    this.keyLength = new short[size];
         }
+	
+	public void setObject(int num, long tableId, byte key[]){
+            this.tableId[num] = tableId;
+            this.key[num] = key;
+	    this.keyLength[num] = (short) this.key[num].length;
+	}
     }
     
     public static class MultiWriteObject {
-        long tableId;
-        byte[] key;
-        byte[] value;
-        RejectRules rules;
+        public long[] tableId;
+        public byte[] key[];
+	public short[] keyLength;
+        public byte[] value[];
+	public short[] valueLength;
+        public RejectRules[] rules;
 
-        public MultiWriteObject(long tableId, byte[] key, byte[] value, RejectRules rules) {
-            this.tableId = tableId;
-            this.key = key;
-            this.value = value;
-            this.rules = rules;
+        public MultiWriteObject(int size) {
+            this.tableId = new long[size];
+            this.key = new byte[size][];
+	    this.keyLength = new short[size];
+            this.value = new byte[size][];
+	    this.valueLength = new short[size];
+            this.rules = new RejectRules[size];
         }
+	
+	public void setObject(int num, long tableId, byte key[], byte value[], RejectRules rules){
+	    this.tableId[num] = tableId;
+	    this.key[num] = key;
+	    this.keyLength[num] = (short) key.length;
+	    this.value[num] = value;
+	    this.valueLength[num] = (short) value.length;
+	    this.rules[num] = rules;
+	}
+	
     }
 
-    public class MultiWriteRspObject {
+    public static class MultiWriteRspObject {
         private int status;
         private long version;
 
@@ -127,7 +148,7 @@
      * or multiple return values, and we don't know the object's size ahead of
      * time, so passing in a fixed-length array would be problematic.
      */
-    public class Object {
+    public static class Object {
         Object(byte[] _key, byte[] _value, long _version)
         {
             key = _key;
@@ -151,6 +172,17 @@
         final public byte[] value;
         final public long version;
     }
+    
+    public static class TableEnumeratorObject {
+	TableEnumeratorObject(Object[] _object, long _nextHash)
+        {
+	    object = _object;
+	    nextHash = _nextHash;
+	}
+	
+	final public Object[] object;
+	final public long nextHash;
+    }
 
     public class TableEnumerator {
         private long tableEnumeratorObjectPointer = 0;
@@ -167,6 +199,43 @@
         public native Object next();
     }
 
+    public class TableEnumerator2 {
+        
+	protected long tableId;
+	protected LinkedList<JRamCloud.Object> rcobjs = null;
+	protected long nextHash = 0;
+	protected boolean done = false;
+	
+        public TableEnumerator2(long tableId)
+        {
+	    this.tableId = tableId;
+	    rcobjs = new LinkedList<>();
+        }
+        public boolean hasNext() {
+	    if (rcobjs.isEmpty()) 
+	    {
+		if (done) {
+		    return false;
+		}
+		JRamCloud.TableEnumeratorObject o = getTableObjects(this.tableId, this.nextHash);
+		if (o.nextHash == 0L) {
+		    done = true;
+		}
+		this.nextHash = o.nextHash;
+		rcobjs.addAll(Arrays.asList(o.object));
+		if (rcobjs.isEmpty()) {
+		    return false;
+		}
+	    }
+	    return true;
+	}
+	
+	public Object next() 
+	{
+	    return rcobjs.pop();
+	}
+    }
+    
     /**
      * Connect to the RAMCloud cluster specified by the given coordinator's
      * service locator string. This causes the JNI code to instantiate the
@@ -294,7 +363,8 @@
     public native long write(long tableId, byte[] key, byte[] value);
     public native long write(long tableId, byte[] key, byte[] value, RejectRules rules);
     public native long writeRule(long tableId, byte[] key, byte[] value, RejectRules rules);
-    public native MultiWriteRspObject[] multiWrite(MultiWriteObject[] mwrite);
+    public native MultiWriteRspObject[] multiWrite(long[] tableId, byte[] key[], short[] keyDataSize, byte[] value[], short[] valueDataSize, int requestNum, RejectRules[] rules);
+    public native TableEnumeratorObject getTableObjects(long tableId, long nextHash);
 
     /*
      * The following exceptions may be thrown by the JNI functions:
@@ -339,7 +409,60 @@
             super(message);
         }
     }
+    
+    public static void tableEnumeratorTest(JRamCloud ramcloud) {
+        long startTime = 0;
+        for (int x = 0 ; x < 2 ; x ++){
+        for(int N = 1000; N < 10000; N += 1000) {
+            long EnumerateTesttable = ramcloud.createTable("EnumerateTest");
+            for(int i = 0 ; i < N ; ++i) {
+                String key = new Integer(i).toString();
+                ramcloud.write(EnumerateTesttable, key.getBytes(), "Hello, World!".getBytes());
+            }
 
+            MultiReadObject mread[] = new MultiReadObject[N];
+            long tableIdList[] = new long[N];
+            byte[] keydata[] = new byte[N][];
+            short keydataSize[] = new short[N];
+	    startTime = System.nanoTime();
+            for (int j = 0 ; j < N ; ++j) {
+                tableIdList[j] = EnumerateTesttable;
+                String key = new Integer(j).toString();
+                keydata[j] = key.getBytes();
+                keydataSize[j] = (short) keydata[j].length;
+            }
+            JRamCloud.Object out[] = ramcloud.multiRead(tableIdList, keydata, keydataSize, N);
+            for (int i = 0; i < N; ++i) {
+                if(out[i].version == 0) {
+                        System.out.println("Verify fail " + out[i].getKey() + " V:" + out[i].getValue());
+                }
+            }
+
+            System.out.println("multiRead           : " + N + " Time : " + (System.nanoTime()-startTime));
+
+            startTime = System.nanoTime();
+            JRamCloud.TableEnumerator tableEnum = ramcloud.new TableEnumerator(EnumerateTesttable);
+            while (tableEnum.hasNext()) {
+                Object tableEntry = tableEnum.next();
+                if (tableEntry != null) {
+                    System.out.println("tableEnumerator object: key = [" + tableEntry.getKey() + "], value = [" + tableEntry.getValue() + "]");
+                }
+            }
+	    System.out.println("old TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
+
+            startTime = System.nanoTime();
+            JRamCloud.TableEnumerator2 tableEnum2 = ramcloud.new TableEnumerator2(EnumerateTesttable);
+	    while (tableEnum2.hasNext()) {
+		Object tableEntry2 = tableEnum2.next();
+		if (tableEntry2 != null) {
+                    System.out.println("tableEnumerator2 object: key = [" + tableEntry2.getKey() + "], value = [" + tableEntry2.getValue() + "]");
+                }
+            }
+            System.out.println("new TableEnumerator : " + N + " Time : " + (System.nanoTime()-startTime));
+            ramcloud.dropTable("EnumerateTest");
+        }
+        }
+    }
     /**
      * A simple end-to-end test of the java bindings.
      */
@@ -391,30 +514,26 @@
         ramcloud.write(tableId6, "object3-1", "value:3-1");
         ramcloud.write(tableId6, "object3-2", "value:3-2");
 
-	long tableIdList[] = new long[2];
-        byte[] keydata[] = new byte[2][];
-        short keydataSize[] = new short[2];
-        tableIdList[0] = tableId4;
-        keydata[0] = "object1-1".getBytes();
-        keydataSize[0] = (short) keydata[0].length;
-        tableIdList[1] = tableId5;
-        keydata[1] = "object2-1".getBytes();
-	keydataSize[1] = (short) keydata[1].length;
+	MultiReadObject mr = new MultiReadObject(2);
+	MultiWriteObject mw = new MultiWriteObject(2);
+	
+	mr.setObject(0, tableId4, "object1-1".getBytes());
+	mr.setObject(1, tableId5, "object2-1".getBytes());
 
-        JRamCloud.Object out[] = ramcloud.multiRead(tableIdList, keydata, keydataSize, 2);
+        JRamCloud.Object out[] = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
         for (int i = 0 ; i < 2 ; i++){
             System.out.println("multi read object: key = [" + out[i].getKey() + "], value = ["
                     + out[i].getValue() + "]");
         }
 
-        MultiWriteObject mwrite[] = new MultiWriteObject[2];
         for (int i = 0; i < 1000; i++) {
             String key1 = "key1" + new Integer(i).toString();
             String key2 = "key2" + new Integer(i).toString();
-      
-            mwrite[0] = new MultiWriteObject(tableId4, key1.getBytes(), "v0-value".getBytes(), null);
-            mwrite[1] = new MultiWriteObject(tableId5, key2.getBytes(), "v1".getBytes(), null);
-            MultiWriteRspObject[] rsp = ramcloud.multiWrite(mwrite);
+	    
+	    mw.setObject(0, tableId4, key1.getBytes(), "v0-value".getBytes(), null);
+	    mw.setObject(1, tableId5, key2.getBytes(), "v1".getBytes(), null);
+      	    
+            MultiWriteRspObject[] rsp = ramcloud.multiWrite(mw.tableId, mw.key, mw.keyLength, mw.value, mw.valueLength, 2, mw.rules);
             if (rsp != null) {
                 for (int j = 0; j < rsp.length; j++) {
                     System.out.println("multi write rsp(" + j + ") status:version " + rsp[j].getStatus() + ":" + rsp[j].getVersion());
@@ -424,19 +543,18 @@
         for (int i = 0; i < 1000; i++) {
             String key1 = "key1" + new Integer(i).toString();
             String key2 = "key2" + new Integer(i).toString();
-            tableIdList[0] = tableId4;
-            keydata[0] = key1.getBytes();
-            keydataSize[0] = (short) keydata[0].length;
-            tableIdList[1] = tableId5;
-            keydata[1] = key2.getBytes();
-            keydataSize[1] = (short) keydata[1].length;
 
-            out = ramcloud.multiRead(tableIdList, keydata, keydataSize, 2);
+	    mr.setObject(0, tableId4, key1.getBytes());
+	    mr.setObject(1, tableId5, key2.getBytes());
+	    
+            out = ramcloud.multiRead(mr.tableId, mr.key, mr.keyLength, 2);
             for (int j = 0; j < 2; j++) {
                 System.out.println("multi read object: key = [" + out[j].getKey() + "], value = [" + out[j].getValue() + "]");
             }
         }
 
+	tableEnumeratorTest(ramcloud);
+	
         ramcloud.dropTable("table4");
         ramcloud.dropTable("table5");
         ramcloud.dropTable("table6");