Optimization of TableEnumerator/MultiWrite

Change-Id: Iefc6cb15d6ee2a16b288a867499d79802320df09
diff --git a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
index fe61007..4694c4e 100644
--- a/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
+++ b/src/main/cpp/edu_stanford_ramcloud_JRamCloud.cc
@@ -340,12 +340,11 @@
 
     const static jmethodID methodId = env->GetMethodID(cls,
                                           "<init>",
-                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                          "([B[BJ)V");
     check_null(methodId, "GetMethodID failed");
 
     return env->NewObject(cls,
                           methodId,
-                          jRamCloud,
                           jKey,
                           jValue,
                           static_cast<jlong>(version));
@@ -377,7 +376,7 @@
                                                         jobject jRamCloud,
 							jlongArray jTableId,
 							jobjectArray jKeyData,
-							jshortArray jKeyDataSize,
+							jshortArray jKeyLength,
 							jint jrequestNum){
 
     RamCloud* ramcloud = getRamCloud(env, jRamCloud);
@@ -386,22 +385,22 @@
     jbyteArray jKey[jrequestNum];
     MultiReadObject* requests[jrequestNum];
 
-    jlong TableId;
-    jshort KeyDataSize;
-    jbyte* data[jrequestNum];
+    jlong tableId;
+    jshort keyLength;
+    jbyte* keyData[jrequestNum];
 
     for (int i = 0 ; i < jrequestNum ; i++){
         jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i); 
 
-        env->GetShortArrayRegion(jKeyDataSize, i, 1, &KeyDataSize);
+        env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
 
-        data[i] = (jbyte *) malloc(KeyDataSize);
-        env->GetByteArrayRegion(jKey[i], 0, KeyDataSize, data[i]);
+        keyData[i] = (jbyte *) malloc(keyLength);
+        env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
 
-        env->GetLongArrayRegion(jTableId, i, 1, &TableId);
-        objects[i].tableId = TableId;
-        objects[i].key = data[i];
-	objects[i].keyLength = KeyDataSize;
+        env->GetLongArrayRegion(jTableId, i, 1, &tableId);
+        objects[i].tableId = tableId;
+        objects[i].key = keyData[i];
+	objects[i].keyLength = keyLength;
         objects[i].value = &values[i];
         requests[i] = &objects[i];
     }
@@ -414,7 +413,7 @@
     check_null(jc_RcObject, "FindClass failed");
     const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
                                         "<init>",
-                                        "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                        "([B[BJ)V");
 
     jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
@@ -425,11 +424,11 @@
 	    check_null(jValue, "NewByteArray failed");
 	    JByteArrayGetter value(env, jValue);
 	    values[i].get()->copy(0, values[i].get()->getTotalLength(), value.pointer);
-	    jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, jKey[i], jValue);
+	    jobject obj = env->NewObject(jc_RcObject, jm_init, jKey[i], jValue);
 	    check_null(obj, "NewObject failed");
 	    env->SetObjectArrayElement(outJNIArray, i, obj);
 	}
-	free(data[i]);
+	free(keyData[i]);
     }
     return outJNIArray;
 }
@@ -672,11 +671,10 @@
         check_null(cls, "FindClass failed");
         const static jmethodID methodId = env->GetMethodID(cls,
                                           "<init>",
-                                          "(L" PACKAGE_PATH "JRamCloud;[B[BJ)V");
+                                          "([B[BJ)V");
         check_null(methodId, "GetMethodID failed");
         return env->NewObject(cls,
                               methodId,
-                              jTableEnumerator,
                               jKey,
                               jValue,
                               static_cast<jlong>(version));        
@@ -686,24 +684,123 @@
 
 /*
  * Class:     edu_stanford_ramcloud_JRamCloud
+ * Method:    getTableObjects
+ * Signature: (JJ)Ledu/stanford/ramcloud/JRamCloud/TableEnumeratorObject;
+ */
+JNIEXPORT jobject JNICALL Java_edu_stanford_ramcloud_JRamCloud_getTableObjects(JNIEnv *env,
+                                                                               jobject jRamCloud,
+                                                                               jlong jTableId,
+                                                                               jlong jTabletNextHash){
+
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+
+    Buffer state;
+    Buffer objects;
+    bool done = false;
+    uint64_t version = 0;
+
+    uint32_t nextOffset = 0;
+
+    const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$Object"));
+    check_null(jc_RcObject, "FindClass failed");
+    const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
+                                        "<init>",
+                                        "([B[BJ)V");
+    check_null(jm_init, "GetMethodID failed");
+    
+    const static jclass jc_RcTableObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$TableEnumeratorObject"));
+    check_null(jc_RcTableObject, "FindClass failed");
+    const static jmethodID jm_TableEnumeratorObject_init = env->GetMethodID(jc_RcTableObject,
+                                        "<init>",
+                                        "([Ledu/stanford/ramcloud/JRamCloud$Object;J)V");
+    check_null(jm_TableEnumeratorObject_init, "GetMethodID failed");
+
+    
+    while (true) {
+        jTabletNextHash = ramcloud->enumerateTable(jTableId, jTabletNextHash, state, objects);
+        if (objects.getTotalLength() > 0) {
+            break;
+        }
+        if (objects.getTotalLength() == 0 && jTabletNextHash == 0) {
+            done = true;
+            break;
+        }    
+    }
+    
+    if (done) {
+        return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, env->NewObjectArray(0, jc_RcObject , NULL), 0);
+    }
+    
+    int numOfTable;
+    for (numOfTable = 0; nextOffset < objects.getTotalLength() ; numOfTable++) {
+        uint32_t objectSize = *objects.getOffset<uint32_t>(nextOffset);
+        nextOffset += downCast<uint32_t>(sizeof(uint32_t));
+        nextOffset += objectSize;
+    }
+    
+    jobjectArray outJNIArray = env->NewObjectArray(numOfTable, jc_RcObject , NULL);
+    check_null(outJNIArray, "NewObjectArray failed");
+
+    nextOffset = 0;
+    for (int i = 0; nextOffset < objects.getTotalLength() ;i++) {
+        uint32_t objectSize = *objects.getOffset<uint32_t>(nextOffset);
+        nextOffset += downCast<uint32_t>(sizeof(uint32_t));
+
+        const void* blob = objects.getRange(nextOffset, objectSize);
+        nextOffset += objectSize;
+
+        Object object(blob, objectSize);
+
+        jbyteArray jKey = env->NewByteArray(object.getKeyLength());
+        jbyteArray jValue = env->NewByteArray(object.getDataLength());
+
+        JByteArrayGetter key(env, jKey);
+        JByteArrayGetter value(env, jValue);
+
+        memcpy(key.pointer, object.getKey(), object.getKeyLength());
+        memcpy(value.pointer, object.getData(), object.getDataLength());
+
+        version = object.getVersion();
+
+        jobject obj = env->NewObject(jc_RcObject, jm_init, jKey, jValue, static_cast<jlong>(version));
+        check_null(obj, "NewObject failed");
+
+        env->SetObjectArrayElement(outJNIArray, i, obj);
+    }
+
+    return env->NewObject(jc_RcTableObject, jm_TableEnumeratorObject_init, outJNIArray, jTabletNextHash);
+
+}
+
+/*
+ * Class:     edu_stanford_ramcloud_JRamCloud
  * Method:    multiWrite
  * Signature: ([Ledu/stanford/ramcloud/JRamCloud/MultiWriteObject;)[Ledu/stanford/ramcloud/JRamCloud/MultiWriteRspObject;
  */
-JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, jobject jRamCloud, jobjectArray jmultiWriteArray) {
-    jint requestNum = env->GetArrayLength(jmultiWriteArray);
-    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
-    Tub<MultiWriteObject> objects[requestNum];
-    MultiWriteObject *requests[requestNum];
-    RejectRules rules[requestNum];
-    jbyteArray jKey[requestNum];
-    jbyteArray jValue[requestNum];
+JNIEXPORT jobjectArray JNICALL Java_edu_stanford_ramcloud_JRamCloud_multiWrite(JNIEnv *env, 
+	                                                                       jobject jRamCloud,
+	                                                                       jlongArray jTableId,
+	                                                                       jobjectArray jKeyData,
+	                                                                       jshortArray jKeyLength,
+	                                                                       jobjectArray jValueData,
+	                                                                       jshortArray jValueLength,
+	                                                                       jint jrequestNum,
+	                                                                       jobjectArray jRules ) {
     
-    const static jclass jc_multiWriteObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteObject"));
+    RamCloud* ramcloud = getRamCloud(env, jRamCloud);
+    Tub<MultiWriteObject> objects[jrequestNum];
+    MultiWriteObject *requests[jrequestNum];
+    RejectRules rules[jrequestNum];
+    jbyteArray jKey[jrequestNum];
+    jbyteArray jValue[jrequestNum];
+    
+    jlong tableId;
+    jshort keyLength;
+    jshort valueLength;
+    jbyte* keyData[jrequestNum];
+    jbyte* valueData[jrequestNum];
+    
     const static jclass jc_RejectRules = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$RejectRules"));
-    const static jfieldID jf_tableId = env->GetFieldID(jc_multiWriteObject, "tableId", "J");
-    const static jfieldID jf_key = env->GetFieldID(jc_multiWriteObject, "key", "[B");
-    const static jfieldID jf_value = env->GetFieldID(jc_multiWriteObject, "value", "[B");
-    const static jfieldID jf_reject_rules = env->GetFieldID(jc_multiWriteObject, "rules", "L" PACKAGE_PATH "JRamCloud$RejectRules;");
 
     const static jfieldID jf_doesntExist = env->GetFieldID(jc_RejectRules, "doesntExist", "Z");
     check_null(jf_doesntExist, "doesentExist field id is null");
@@ -716,21 +813,20 @@
     const static jfieldID jf_versionNeGiven = env->GetFieldID(jc_RejectRules, "versionNeGiven", "Z");
     check_null(jf_versionNeGiven, "versionNeGiven field id is null");
 
-    for (int i = 0; i < requestNum; i++) {
-        jobject obj = env->GetObjectArrayElement(jmultiWriteArray, i);
-        check_null(obj, "multi write GetObjectArrayElement failed");
+    for (int i = 0; i < jrequestNum; i++) {
+	env->GetLongArrayRegion(jTableId, i, 1, &tableId);
 
-        uint64_t tableId = env->GetLongField(obj, jf_tableId);
-
-        jKey[i] = (jbyteArray)env->GetObjectField(obj, jf_key);
-        jbyte* keyData = env->GetByteArrayElements(jKey[i], NULL);
-        uint16_t keyLength = env->GetArrayLength(jKey[i]);
-
-        jValue[i] = (jbyteArray)env->GetObjectField(obj, jf_value);
-        jbyte* valueData = env->GetByteArrayElements(jValue[i], NULL);
-        uint32_t valueLength = env->GetArrayLength(jValue[i]);
-
-        jobject jRejectRules = env->GetObjectField(obj, jf_reject_rules);
+	env->GetShortArrayRegion(jKeyLength, i, 1, &keyLength);
+	jKey[i] = (jbyteArray)env->GetObjectArrayElement(jKeyData, i);
+	keyData[i] = (jbyte *) malloc(keyLength);
+	env->GetByteArrayRegion(jKey[i], 0, keyLength, keyData[i]);
+	
+	env->GetShortArrayRegion(jValueLength, i, 1, &valueLength);
+        jValue[i] = (jbyteArray)env->GetObjectArrayElement(jValueData, i);
+	valueData[i] = (jbyte *) malloc(valueLength);
+	env->GetByteArrayRegion(jValue[i], 0, valueLength, valueData[i]);
+	
+	jobject jRejectRules = (jbyteArray)env->GetObjectArrayElement(jRules, i);
         rules[i] = {};
 
         if (jRejectRules != NULL) {
@@ -750,28 +846,28 @@
             ruleBool = env->GetBooleanField(jRejectRules, jf_versionNeGiven);
             rules[i].versionNeGiven = ruleBool ? 1 : 0;
         }
-        objects[i].construct(tableId, keyData, keyLength, valueData, valueLength, &rules[i]);
+        objects[i].construct(tableId, keyData[i], keyLength, valueData[i], valueLength, &rules[i]);
         requests[i] = objects[i].get();
     }
     try {
-        ramcloud->multiWrite(requests, requestNum);
+        ramcloud->multiWrite(requests, jrequestNum);
     } EXCEPTION_CATCHER(NULL);
  
     const static jclass jc_RcObject = (jclass)env->NewGlobalRef(env->FindClass(PACKAGE_PATH "JRamCloud$MultiWriteRspObject"));
     check_null(jc_RcObject, "FindClass failed");
     const static jmethodID jm_init = env->GetMethodID(jc_RcObject,
                                         "<init>",
-                                        "(L" PACKAGE_PATH "JRamCloud;IJ)V");
+                                        "(IJ)V");
 
-    jobjectArray outJNIArray = env->NewObjectArray(requestNum, jc_RcObject , NULL);
+    jobjectArray outJNIArray = env->NewObjectArray(jrequestNum, jc_RcObject , NULL);
     check_null(outJNIArray, "NewObjectArray failed");
     
-    for (int i = 0 ; i < requestNum ; i++) {
-        jobject obj = env->NewObject(jc_RcObject, jm_init, jRamCloud, objects[i]->status, objects[i]->version);
+    for (int i = 0 ; i < jrequestNum ; i++) {
+        jobject obj = env->NewObject(jc_RcObject, jm_init, objects[i]->status, objects[i]->version);
         check_null(obj, "NewObject failed");
         env->SetObjectArrayElement(outJNIArray, i, obj);
-        env->ReleaseByteArrayElements(jKey[i], (jbyte *)requests[i]->key, JNI_ABORT);
-        env->ReleaseByteArrayElements(jValue[i], (jbyte *)requests[i]->value, JNI_ABORT);
+	free(keyData[i]);
+	free(valueData[i]);
         objects[i].destroy();
     }
     return outJNIArray;
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
index 26f1418..ad519c8 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudGraph.java
@@ -247,16 +247,18 @@
 	    }
 	    vertices.add(v);
 	}
-	MultiWriteObject multiWriteObjects[] = new MultiWriteObject[vertices.size() * 2];
+	
+	MultiWriteObject mwo = new MultiWriteObject(vertices.size()*2);
+		
 	for (int i=0; i < vertices.size(); i++) {
 	    RamCloudVertex v = vertices.get(i);
-	    multiWriteObjects[i*2] = new MultiWriteObject(vertTableId, v.rcKey, ByteBuffer.allocate(0).array(), null);
-	    multiWriteObjects[i*2+1] = new MultiWriteObject(vertPropTableId, v.rcKey, ByteBuffer.allocate(0).array(), null);
+	    mwo.setObject(i*2, vertTableId, v.rcKey, ByteBuffer.allocate(0).array(), null);
+	    mwo.setObject(i*2+1, vertTableId, v.rcKey, ByteBuffer.allocate(0).array(), null);
 	}
 	try {
 		PerfMon pm = PerfMon.getInstance();
 		pm.multiwrite_start("RamCloudVertex create()");
-	    getRcClient().multiWrite(multiWriteObjects);
+		getRcClient().multiWrite(mwo.tableId, mwo.key, mwo.keyLength, mwo.value, mwo.valueLength, vertices.size()*2, mwo.rules);
 		pm.multiwrite_end("RamCloudVertex create()");
 	    log.info("ramcloud vertices are created");
 	} catch (Exception e) {
diff --git a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
index b41dca8..03c4ba3 100644
--- a/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
+++ b/src/main/java/com/tinkerpop/blueprints/impls/ramcloud/RamCloudVertex.java
@@ -490,12 +490,12 @@
 		// TODO: Existence check costs extra (presently 2 reads), could use option to turn on/off
 		if (!exists()) {
 			PerfMon pm = PerfMon.getInstance();
-			JRamCloud vertTable = graph.getRcClient();
-			MultiWriteObject[] mwo = new MultiWriteObject[2];
-			mwo[0] = new MultiWriteObject(graph.vertTableId, rcKey, ByteBuffer.allocate(0).array(), null);
-			mwo[1] = new MultiWriteObject(graph.vertPropTableId, rcKey, ByteBuffer.allocate(0).array(), null);
+			JRamCloud vertTable = graph.getRcClient();			
+			MultiWriteObject mwo = new MultiWriteObject(2);
+			mwo.setObject(1, graph.vertTableId, rcKey, ByteBuffer.allocate(0).array(), null);
+			mwo.setObject(2, graph.vertPropTableId, rcKey, ByteBuffer.allocate(0).array(), null);
 			pm.multiwrite_start("RamCloudVertex create()");
-			vertTable.multiWrite(mwo);
+			vertTable.multiWrite(mwo.tableId, mwo.key, mwo.keyLength, mwo.value, mwo.valueLength, 2, mwo.rules);
 			pm.multiwrite_end("RamCloudVertex create()");
 		} else {
 			throw ExceptionFactory.vertexWithIdAlreadyExists(id);
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");
diff --git a/src/main/java/net/onrc/onos/datastore/RCObject.java b/src/main/java/net/onrc/onos/datastore/RCObject.java
index a990978..a2d8c6e 100644
--- a/src/main/java/net/onrc/onos/datastore/RCObject.java
+++ b/src/main/java/net/onrc/onos/datastore/RCObject.java
@@ -18,12 +18,14 @@
 import com.esotericsoftware.kryo.io.Output;
 
 import edu.stanford.ramcloud.JRamCloud;
+import edu.stanford.ramcloud.JRamCloud.MultiReadObject;
 import edu.stanford.ramcloud.JRamCloud.MultiWriteObject;
 import edu.stanford.ramcloud.JRamCloud.MultiWriteRspObject;
 import edu.stanford.ramcloud.JRamCloud.ObjectDoesntExistException;
 import edu.stanford.ramcloud.JRamCloud.ObjectExistsException;
 import edu.stanford.ramcloud.JRamCloud.RejectRules;
 import edu.stanford.ramcloud.JRamCloud.TableEnumerator;
+import edu.stanford.ramcloud.JRamCloud.TableEnumerator2;
 import edu.stanford.ramcloud.JRamCloud.WrongVersionException;
 
 /**
@@ -304,21 +306,17 @@
 	JRamCloud rcClient = RCClient.getClient();
 
 	final int reqs = req.size();
-
-        long tableId[] = new long[reqs];
-        byte[] key[] = new byte[reqs][];
-        short keySize[] = new short[reqs];
+	
+	MultiReadObject multiReadObjects = new MultiReadObject(req.size());
 
 	// setup multi-read operation
 	for (int i = 0; i < reqs; ++i) {
 	    RCObject obj = req.get(i);
-            tableId[i] = obj.getTableId();
-            key[i] = obj.getKey();
-            keySize[i] = (short) key[i].length;
+            multiReadObjects.setObject(i, obj.getTableId(), obj.getKey());
 	}
 
 	// execute
-	JRamCloud.Object results[] = rcClient.multiRead(tableId, key, keySize, reqs);
+	JRamCloud.Object results[] = rcClient.multiRead(multiReadObjects.tableId, multiReadObjects.key, multiReadObjects.keyLength, reqs);
 	assert (results.length <= req.size());
 
 	// reflect changes to RCObject
@@ -420,10 +418,10 @@
     private static boolean multiWriteInternal(ArrayList<WriteOp> ops) {
 
 	boolean fail_exists = false;
-	MultiWriteObject multiWriteObjects[] = new MultiWriteObject[ops.size()];
+	MultiWriteObject multiWriteObjects = new MultiWriteObject(ops.size());
 	JRamCloud rcClient = RCClient.getClient();
 
-	for (int i = 0; i < multiWriteObjects.length; ++i) {
+	for (int i = 0; i < ops.size(); ++i) {
 	    WriteOp op = ops.get(i);
 	    RCObject obj = op.getObject();
 
@@ -442,12 +440,11 @@
 		rules.setNeVersion(obj.getVersion());
 		break;
 	    }
-	    multiWriteObjects[i] = new MultiWriteObject(obj.getTableId(),
-		    obj.getKey(), obj.getValue(), rules);
+	    multiWriteObjects.setObject(i, obj.getTableId(), obj.getKey(), obj.getValue(), rules);
 	}
 
-	MultiWriteRspObject[] results = rcClient.multiWrite(multiWriteObjects);
-	assert (results.length == multiWriteObjects.length);
+	MultiWriteRspObject[] results = rcClient.multiWrite(multiWriteObjects.tableId, multiWriteObjects.key, multiWriteObjects.keyLength, multiWriteObjects.value, multiWriteObjects.valueLength, ops.size(), multiWriteObjects.rules);
+	assert (results.length == ops.size());
 
 	for (int i = 0; i < results.length; ++i) {
 	    WriteOp op = ops.get(i);
@@ -467,17 +464,17 @@
 
 	return fail_exists;
     }
-
+   
     public static abstract class ObjectIterator<E extends RCObject> implements
 	    Iterator<E> {
 
-	protected TableEnumerator enumerator;
+	protected TableEnumerator2 enumerator;
 
 	public ObjectIterator(RCTable table) {
 	    // FIXME workaround for JRamCloud bug. It should have been declared
 	    // as static class
 	    JRamCloud c = RCClient.getClient();
-	    this.enumerator = c.new TableEnumerator(table.getTableId());
+	    this.enumerator = c.new TableEnumerator2(table.getTableId());
 	}
 
 	@Override
@@ -493,7 +490,7 @@
 //	    obj.setValueAndDeserialize(o.value, o.version);
 //	    return obj;
 //	}
-
+	
 	@Deprecated
 	@Override
 	public void remove() {